Day17 ajax-下

回调函数的嵌套

回调函数

  • 概念:
    => 把 函数A 当做实参, 传递到 函数B 内
    => 在 函数B 内用形参来调用 函数A
    => 这个行为我们叫做回调函数
    => 我们把 函数A 叫做 函数B 的回调函数
  • 原因:
    => 因为 JS 是单线程(同一个时间只能做一个事情)
    => 当我需要在一个异步的末尾做一些事情, 没有办法控制
  • 意义:
    => 在 封装 异步代码的时候
    => 需要在 异步的末尾 做一些事情
    => 使用回调函数的形式进行封装
  • 回调函数的缺点

    • 回调地狱

    • 当回调函数嵌套过多的时候, 会出现回调地狱

    • 导致代码的可维护性和可阅读性降低


// 封装一个函数, 用定时器模拟
 function maibaozhi(jinnang = () => {}) {

  const time = Math.floor(Math.random() * 4000 + 1000)

//   // 定时器: 不固定 1 ~ 5 秒之间执行定时器函数
   setTimeout(() => {

     console.log('我买完报纸了, 本次用时 : ', time)

     // 把你该做的事情做完了, 然后按照 jinnang 执行
     jinnang()

   }, time)
 }

// // 让班长第一次买报纸, 在帮我买一瓶水
maibaozhi(function () { console.log('买水') })
// // 让班长第二次买报纸, 在帮我买一个面包
maibaozhi(function () { console.log('买面包') })
 // 让班长第三次买报纸
 maibaozhi()

常见的典型异步回调

    setTimeout(function(){
        console.log('1s之后做的事情');
        setTimeout(function(){
            console.log('2s之后做的事情');
            setTimeout(function(){
               console.log('3s之后做的事情');
            },1000);
        },1000);
    },1000);

回调地狱

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lUnebM0Z-1663213523325)(/1567134159892.png)]

在回调函数中嵌套,就是上一个函数执行完成,再执行下一个

promise使用(掌握)

在JavaScript的世界中,所有代码都是单线程执行的。由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现.

div.onclick = function(){}

Promise使ES6中引入的一个异步编程解决方案,与传统的ES5方法相比它的结构更合理,功能更强大.

特点

Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态.

语法:

  var p = new Promise(function(resolve,reject){})
 resolve 代表 决定(成功); reject 代表 失败

使用

基本使用
    const p  = new Promise(function(resolve,reject){
        setTimeout(function(){
           // resolve('我是成功的')
           reject('这是失败的');
        },2000)
    });

.then(callback)的使用(成功时调用)

.catch(callback的用法(失败时调用)

  p.then(function(data){
			console.log('resolved成功回调');
			console.log('成功回调接受的值:',data);
		}).catch(function(reason, data){
		    console.log('catch到rejected失败回调');
		     console.log('catch失败执行回调抛出失败原因:',reason);
	});	
效果和写在then的第二个参数里面一样。它将大于10的情况下的失败回调的原因输出

注意:resolve和reject的两种状态

resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
工作中常用方式,结合函数

我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数

 let promiseFn =()=>{
	 console.log('点击方法被调用')
	 let p = new Promise(function(resolve, reject){
		//做一些异步操作
		setTimeout(function(){
				console.log('执行完成Promise');
				resolve('成功的时候调用');
			}, 2000);
		});
        return p
	}

promiseFn().then(function(data){
     console.log(11111)
}).catch(function(data){
     console.log(2222)
})

静态方法的使用(掌握 )

promise并非一开始就必须处于待定的状态,然后通过执行器函数才能转化为成功或失败.也可以直接调用resolve和reject方法.

Primise.resolve()
let p1 = new Promise((resolve,reject)=> resolve());
let p2 = Promise.resolve()
// 两种方式结果是一样的.都表示状态切换到成功
Primise.reject()
let p1 = new Promise((resolve,reject)=> reject());
let p2 = Promise.reject()
// 都表示状态切换到失败
Primise.finally()

该方法无论期约为成功或者失败都可以调用,主要是表面then和catch当中出现冗余的代码.但它无法知道状态是成功还是失败,因此该方法主要用于添加清理代码.

		   let p1 = Promise.resolve();
		   let p2 =Promise.reject(); // 错误没有被处理  Uncaught (in promise)
		   
		   p1.finally(function(){
			   console.log('被调用...');
		   })
		
		   p2.finally(function(){
			   console.log('可以...');
		   })
			
promise改造ajax依赖调用

使用promise检测ajax的请求状态,利用promise的成功状态函数,返回ajax请求到的数据

      const ajaxParam=param=>{
    return new Promise(function(resolve,reject){
        let xhr = new XMLHttpRequest();
      xhr.open('get',param.url); // 默认是true,表示异步
  
      xhr.send();
      xhr.onreadystatechange = function(){
          if(xhr.readyState==4 ){
              if(xhr.status==200){
                  resolve(xhr.responseText)
              }else{
                  reject(xhr.responseText)
              }

          }
      }
});
          }

期约的连锁和合成(了解)

基本形式

每个期约的实例方法(then(),catch(),finally())都会返回一个新的期约对象,而这些期约对象又有自己的实例方法,如此连起来就是’期约连锁’

           let p = new Promise((resolve,reject)=>{
			   console.log('first');
			   resolve();
		   });
		   p.then(()=>console.log('second'))
		   .then(()=>console.log('third'))
		   .then(()=>console.log('forth'))
	以上执行,四个同步的任务,	

串行调用

           let p = new Promise((resolve,reject)=>{
			   //调用失败
			 reject();
			 // 调用resolve() 则不能在处理结果后调用catch
		   });
		   // catch() 处理错误,返回的promise,可以再调用then()
		   p.catch(()=>console.log('reject handler'))
		   .then(()=>console.log('resolve handler'))
		   .finally(()=>console.log('finally handler'))
期约图

一个期约可以有任意多个处理程序,一个处理程序就是一个节点,每一个节点都会等待前一个节点的落定,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8wdRtQVZ-1663213523326)(/1649060457753.png)]

        let A = new Promise((resolve,reject)=>{
			  console.log('A');
			  resolve();
		   });
		   
		   let B=A.then(()=>console.log('B'));
		   let C=A.then(()=>console.log('C'));
		   
		   B.then(()=>console.log('D'));
		   B.then(()=>console.log('E'));
		   C.then(()=>console.log('F'));
		   C.then(()=>console.log('G'));
		   
结合ajax使用

当第一次发送的网络请求得到的数据,为第二次请求所必须的数据,那么此时我们就要串联使用了.

  • 发送请求, 请求测试接口1, 结果打印在控制台

  • 发送请求, 请求测试接口2, 结果打印在控制台

  • 发送请求, 请求测试接口3, 结果打印在控制台

    => 前提: 必须要在第二个请求完成以后, 在发送第三个请求

    Promise 链式调用

    • 当你在第一个 then 内部返回一个新的 Promise 对象的时候
       可以把新的 Promise 的 then 连续书写

      pAjax({ url: 'http://localhost:8888/test/first' })
           .then(res => {
             console.log(res)
             // 这里是第一个请求完成后会执行的代码
     
             // 调用 pAjax, 创建的第二个 承若(Promise)
             // 在第一个 then 里面把新的 Promise return 了
             return pAjax({ url: 'http://localhost:8888/test/second', dataType: 'json' })
           })
           .then(res2 => {
             console.log(res2)
     
             // 这里是第二个请求完成后会执行的代码
     
             // 调用 pAjax, 创建第三个 承诺(Promise)
             // 在第二个 then 里面被写的 Promise return 了
             return pAjax({ url: 'http://localhost:8888/test/third', data: 'name=Jack&age=20', dataType: 'json' })
           })
           .then(res3 => console.log(res3))
   

期约的合并

Promise.all()

所有的异步请求完成才执行的方法.

Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。

let wake = (time) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${time / 1000}秒后醒来`)
    }, time)
  })
}

let p1 = wake(3000);
let p2 = wake(2000);

Promise.all([p1, p2]).then((result) => {
  console.log(result)       // [ '3秒后醒来', '2秒后醒来' ]
}).catch((error) => {
  console.log(error)
})
只有两次调用promise都执行完毕,才会执行all
Promise.race()

Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  },1000)
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('failed')
  }, 500)
})

Promise.race([p1, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)  // 打开的是 'failed'
})

期约的错误处理

同步代码的错误

try…catch…只能捕获同步代码的错误,而对于异步它们是无能无力的

		try{
			    throw new Error('错误抛出');	
			}catch(error){
				console.log(error);
			}

上下不要一起运行
		try{
			    Promise.reject( new Error('错误抛出')	)
			}catch(error){
				console.log(error);
			}

这里的同步代码之所以没有捕获期约抛出的错误,是因为它们没有通过异步模式捕获错误.

期约既能在同步代码中使用,但也是异步执行的媒介.

期约错误的处理

抛出错误会被catch直接获取

           let A = new Promise((resolve,reject)=>{
			  resolve();
		   });
		   
		   A.then(()=>{
			   throw new Error('then中的错误')
		   }).catch(info=>{
			   console.log(info);
		   })

async 和await的使用(重点)

是什么

async 和 await 关键字

    + 只是对 Promise 语法的一种增强书写方式
            + 注意: 作用, 为了把异步代码写的看起来像同步

  async 关键字
    + 在函数前面书写
    + 作用: 为了在该函数内可以使用 await 关键字

  await 关键字
    + await 后面等待的必须是一个 Promise 对象
    + 目的: 为了等到 后面的 Promise 执行完毕在继续执行下一行代码
    + 作用: 本来应该在 then 里面接受的结果, 可以直接定义变量接受了
    + 解析:当js运行遇到await关键字时,会记录在哪里暂停执行,等await右边的值可用时,js会想消息队列推送任务,这个任务恢复异步的执行.
怎么用
    async function fn() {
      console.log('start')
      // 等着第一个请求
      const r1 = await pAjax({ url: 'http://localhost:8888/test/first' })
      console.log(r1)

      // 如果上面的 请求没有结束, 那么这里的代码不会执行
      // 这里的代码执行了, 一定是因为 await 等到了上面的请求结束
      const r2 = await pAjax({ url: 'http://localhost:8888/test/second', dataType: 'json' })
      console.log(r2)

      const r3 = await pAjax({ url: 'http://localhost:8888/test/third', data: 'name=Jack&age=20', dataType: 'json' })
      console.log(r3)

      console.log('end')
    }

ajax的同源策略

同源策略的基本概念

1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。
同源策略:最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"。

协议相同
域名相同
端口相同

举例来说,这个网址http://www.example.com/dir/page.html协议是http

域名是www.example.com,端口是80(默认端口可以省略)。它的同源情况如下。

http://www.example.com/dir2/other.html:同源

file:///F:/phpStudy/WWW/day01/04-demo/04.html 不同源(协议不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)
同源策略的目的

同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。

同源策略的限制范围

随着互联网的发展,“同源策略”越来越严格,目前,如果非同源,以下三种行为都将收到限制。

1. Cookie、LocalStorage 和 IndexDB 无法读取。
2. DOM 无法获得。
3. AJAX 请求在浏览器端有跨域限制
 虽然这些限制是很有必要的,但是也给我们日常开发带来不好的影响。比如实际开发过程中,往往都会把服务器端架设到一台甚至是一个集群的服务器中,把客户端页面放到另外一个单独的服务器。那么这时候就会出现不同源的情况,如果我们知道两个网站都是安全的话,我们是希望两个不同源的网站之间可以相互请求数据的。这就需要使用到**跨域** 。

jsonp跨域 json+script

什么是跨域

域:表示不同的域名即网址不一样,如:www.baidu.com想访问www.edrc.cn就是不行的.

   现在有两个网址 第一个 192.168.0.1  第二个 192.168.0.2 那么如果要在第一个的ip下.通过ajax访问第二个域名也是不可以的.

 这是w3c组织为了安全性,做出的限制

• Ajax技术由于受到浏览器的限制(为了安全考虑),该方法不允许跨域通信。

在两个不同域名之间互相,发ajax请求.

如何解决跨域

一个公司,有服务器a, 服务器b,如a服务器要引用b服务器上的图片

<img src=”http://www.b.com/a.jpg” />甚至这个图片的路径也可以是线上的.

因为src标签 是没有同源限制。可以借助与script标签中的src属性来实现跨域问题.

在server/src/app.js中修改添加配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qrJSRpIo-1663213523328)(/1646289400339.png)]

app.use('/gx',(req,res)=>{
	  console.log(req.query.cb)
	  res.send(req.query.cb+'({"code":1,"message":"登录成功"})');
	  res.end();
})

1 使用script标签发送请求

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <script>
      function fn(data){
          console.log(data)
      }
      //凡是通过地址栏传参的都是get方式
    </script>
    <script src="http://localhost:8888/gx?cb=fn"></script>
</body>
</html>
JOSNP跨域

json + 动态script

1 JSON可以传输大量数据。

2 script标签 是没有同源策略限制

思路

在需要用到外部跨域,再生成 script标签.

实际操作

script标签没有同源性问题,我们可以借助script标签中的src属性来实现跨域问题。为了灵活使用跨域,一般使用动态生成SCRIPT标签来引用外部JS来实现跨域。

JSONP是指客户端用GET方式传递一个callback参数给服务端,然后在服务端返回数据时将这个callback参数作为函数名来包裹住JSON数据来返回给客户端(生成JS代码),然后客户端,用JS动态生成script标签,并指定该script的src属性等于服务器返回的数据(服务器生成的JS代码),这样服务器端返回的数据,就可以成为脚本的一部分。并将该这样客户端就通过callback函数来处理返回数据了。

将上面的script标签,进行动态生成


面试题:说一说json与jsonp的一个区别?

 答:json是一种通用的数据交换格式,主要实现数据的传输与储存。
	jsonp是一种非官方协议,主要为了解决Ajax跨域请求问题
请求淘宝或百度搜索下拉提示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zqPjob1e-1663213523328)(/1563178861627.png)]

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>



  <style>
    * {
      margin: 0;
      padding: 0;
    }


    #search {
      width: 600px;
      margin: 0 auto;
      margin-top: 300px;
      position: relative;
    }

    #search input {

      width: 480px;
      height: 100%;
      border: 1px solid #b6b6b6;
      height: 20px;
      padding: 9px 7px;
      font: 16px arial;
      border: 1px solid #b8b8b8;
      border-bottom: 1px solid #ccc;
      border-right: 0;
      vertical-align: top;
      outline: none;
      box-shadow: none;
      -webkit-appearance: textfield;
      background-color: white;
      -webkit-rtl-ordering: logical;
      user-select: text;

    }

    #search button {
      cursor: pointer;
      box-sizing: border-box;
      width: 97px;
      height: 40px;
      line-height: 38px;
      padding: 0;
      border: 0;
      background: none;
      background-color: #38f;
      font-size: 16px;
      color: white;
      box-shadow: none;
      font-weight: normal;
      margin-left: -20px;
    }

    .result {
      position: absolute;

      padding: 9px 7px;
      background: #ddd;

    }

    .search-res {
      position: absolute;
      top: 100%;
      left: 0;
      width: 480px;
      border: 1px solid #b6b6b6;
      border-top: none;
    }

    .search-res li {
      list-style-type: none;
      line-height: 20px;
      padding: 2px 5px;
      border-bottom: 1px solid #b6b6b6;
    }
  </style>

  <script>

    window.onload = function () {

      var oButton = $("#search").children[0];
      var oText = $("#search").children[0];
      var timerOut;
      oText.oninput = function () {
        //获取数据
        //延时获取; >> 防止事件多次触发请求冗余过多;
        getRes(oText.value);//获取数据;

      }
    }
    function getRes(val) {
      var script = document.createElement("script");
      script.src = "https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=" + val + "&json=1&p=3&sid=22084_1436_13548_21120_22036_22073&req=2&csor=0&cb=callback";
      document.getElementsByTagName("head")[0].appendChild(script);
    }
    function callback(res) {//回调函数;
      console.log(res.s);//回调数组 中都是关键词字符串;
      var str = "";
      for (var i = 0; i < res.s.length; i++) {
        str += "<li>" + res.s[i] + "</li>";
      }
      $(".search-res").innerHTML = str;
    }
    function $(select) {
      return document.querySelector(select);
    }

  </script>

</head>

<body>

  <div id="search">
    <input type="text">
    <button>嗖嗖嗖一下</button>
    <ul class="search-res">

    </ul>
  </div>



</body>

</html>
ajax的兼容(了解)

现在一般最多兼容到 IE8, 这里以后见到了知道是在处理兼容性就行了

var xhr = null;
if(XMLHttpRequest){
  //现代浏览器 IE7+
  xhr = new  XMLHttpRequest();
}else{
  //老版本的 Internet Explorer (IE5 和 IE6)使用 ActiveX 对象:
  xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}

作业:

1 掌握jsonp跨域原理

2 实现搜索下拉框的提示此词消失

“;
for (var i = 0; i < res.s.length; i++) {
str += “

  • ” + res.s[i] + “
  • ”;
    }
    $(”.search-res").innerHTML = str;
    }
    function $(select) {
    return document.querySelector(select);
    }

    ```
    ajax的兼容(了解)

    现在一般最多兼容到 IE8, 这里以后见到了知道是在处理兼容性就行了

    var xhr = null;
    if(XMLHttpRequest){
      //现代浏览器 IE7+
      xhr = new  XMLHttpRequest();
    }else{
      //老版本的 Internet Explorer (IE5 和 IE6)使用 ActiveX 对象:
      xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    

    作业:

    1 掌握jsonp跨域原理

    2 实现搜索下拉框的提示此词消失

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值