Ajax 与 Promise(更新中)

536 阅读6分钟

Ajax

1. 介绍

AJAX,是 Asynchronous JavaScript And XML,意思是利用JavaScript执行异步网络请求,对页面进行局部更新,而不需要重载页面。

AJAX是浏览器的功能,利用浏览器在window上增加的XMLHttpRequest函数,利用该函数构造出一个对象,使用该对象进行请求发送与响应接受。

2. 使用

  1. 创建一个XMLHttpRequest对象
  2. 调用对象的open方法启用。
  3. 监听对象的 onreadystatechange 事件(或者是onloadonerror事件),处理函数对返回的数据进行处理。
  4. 调用对象的send方法发送请求。 完整版:
var request = new XMLHttpRequest()
request.open('GET','/xxx')
request.onreadystateChange = function(
    if(request.readyState === 4){
        console.log('请求完成')
        if(request.responese.status >= 200 && request.response.status < 300){
            console.log('请求成功')
        }else{
            console.log('请求失败')
        }
    }
)
request.send()

3. onreadyState状态值

uTools_1684761370864.png

创建 -> 打开 -> 发送 -> 接收 -> 完成

每一次state的改变都会触发readystatechange事件,但我们一般只关心state为4的阶段,此阶段数据接收已完成,可来进行数据处理。onload函数也会在请求完成后即state为4的时候调用。

终止请求

使用XHR对象的abort方法会终止该请求,state会被重置为0。

Promise

异步与回调

  1. 回调函数: 当一个函数作为参数传入另一个参数中,并且它不会立即执行,只有当满足一定条件后该函数才可以执行,这种函数就称为回调函数。我们熟悉的定时器和Ajax中就存在有回调函数:
        setTimeout(function(){ 
            console.log('执行了回调函数'); 
        },3000)

这里的function(){console.log('执行了回调函数')}就是回调函数,它只有在3秒后才会执行

    /1.创建异步对象
    			var xhr=new XMLHttpRequest();
          //2.绑定监听事件(接收请求)
    			xhr.onreadystatechange=function(){
    				//此方法会被调用4次
    				//最后一次,readyState==4
    				//并且响应状态码为200时,才是我们要的响应结果 xhr.status==200
    				if(xhr.readyState==4 && xhr.status==200){
    					//把响应数据存储到变量result中
    					var result=xhr.responseText;
    					console.log(result);
    				}
    			}
          //3.打开链接(创建请求)
    			xhr.open("get","/demo/ajaxDemo",true);
          //4.发送请求
    			xhr.send();

这里的回调函数是xhr.onreadystatechange绑定的函数,在xhr.send()发送请求并拿到响应后执行。

  1. 异步任务 与之相对应的概念是“同步任务”,同步任务在主线程上排队执行,只有前一个任务执行完毕,才能执行下一个任务。异步任务不进入主线程,而是进入异步队列,前一个任务是否执行完毕不影响下一个任务的执行。同样,还拿定时器作为异步任务举例:
setTimeout(function(){
    console.log('执行了回调函数');
},3000)
console.log('111')

结果输出为先111,后“执行了回调函数”。

这种不阻塞后面任务执行的任务就叫做异步任务。

回调地狱

要使用异步输出“武林要以和为贵,要讲武德,不要搞窝里斗”

setTimeout(function () {  //第一层
    console.log('武林要以和为贵');
    setTimeout(function () {  //第二程
        console.log('要讲武德');
        setTimeout(function () {   //第三层
            console.log('不要搞窝里斗');
        }, 1000)
    }, 2000)
}, 3000)

可以看到,代码中的回调函数套回调函数,居然套了3层,这种回调函数中嵌套回调函数的情况就叫做回调地狱。

总结一下,回调地狱就是为是实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差,后期不好维护。

那该如何解决回调地狱呢?下面我们介绍Promise

Promise

promise特点

  1. promise是ES6用来解决异步问题,将异步操作以同步流程表达出来,避免了回调地狱的一个对象。它可以解决异步问题,但不能说promise是异步的。

  2. 一个 Promise 必然处于以下几种状态之一:

  • 待定(pending) :初始状态,既没有被兑现,也没有被拒绝。
  • 已成功(resolved) :意味着操作成功完成。
  • 已拒绝(rejected) :意味着操作失败。 一旦状态改变就不会再变,任何时候都是这个结果。状态改变只能是:pending --> resolved / rejected
  1. promise内部发生错误,不会影响到外部程序的执行。

  2. 无法取消Promise。一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

基础用法

创建promise实例时,必须传入一个函数作为参数

new Promise(() => {});
new Promise();

该函数可以接受另外两个由js引擎提供的函数,resolvereject

resolve:将Promise对象的状态从pending变为resolved,将异步操作的结果作为参数传出去

reject:将Promise对象状态从pending变为rejected,将异步操作报出的错误作为参数传出去

let promise = new Promise((resolve,reject) => {
    // do something
    if(true){
        // 将参数返回,供.then方法调用
        resolve("value");
    } else {
        reject("error");
    }
});

promise实例生成后,可用then方法分别指定resolved状态和rejected状态的回调函数

promise.then(
    value => {
        //resolved时调用,value为resolved函数时返回的参数
        console.log(value);
    },
    err => {
        // rejectd时调用,err为reject函数返回的参数
        console.log(error);
    }
);

then方法只有一个函数参数时,为resolved状态的回调方法

promise.then(value => {
    // 只有状态为resolved才能调用,如果返回的是rejected状态,则报错Uncaught(in promise)error
    console.log(value);
});

只有当promise状态变为resolved或者rejected时才会调用then方法

Promise新建后就会立即执行,并且调用resolve或reject参数的执行。

let promise = new Promise(function(resolve) {
    console.log("Promise");
    resolve();    // resolve调用以后才会执行then方法
    console.log("!!!");
});

promise.then(function() {
    console.log("resoled");
});
console.log("Hi");

微信截图_20230602200117.png

resolve返回的是另一个promise实例

const p1 = new Promise((_, reject) => {
    setTimeout(() => reject('error'), 3000));
});

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => resolve(p1), 1000);
});

p2.then(
    result => console.log(result),
    error => console.log(error)
);

上面的代码中,p1是一个promise,3s变为rejected。p2状态在1s后变为resolved,返回p1。由于p2返回的是另一个promise,导致p2自己的状态失效了,由p1的状态决定p2的状态。所以后面的then语句都变成针对后者p1。也就是说p2在变成resolved以后2s又变成了rejected,导致触发catch方法指定的回调函数。

使用案例:

const task = new Promise((resolve, reject) => {
       setTimeout(() => {
           resolve('成功');
       }, 3000);
   })

   task.then((res) => {
       // 3秒后打印成功 console.log(res);
   })

  // 看这个例子似乎没什么特别之处,感觉就是把callback换成resolve, 那么再看看下面一个
  // 获取列表之前,需要先判断是否登录,then的链式调用 

  new Promise((resolve, reject) => {
       $.ajax('/api/user/login', {
           success: function(isLogin) {
               if (isLogin) {
                   resolve(data);
               } else {
                   reject('没有登录');
               }
           }
       })
   }).then(
       res => {
           $.ajax('/api/list', {
               success: function(data) {
                   resolve(data);
               }
           })
       },
       reason => {
           alert(reason)
       }
   ).then((data) => {
       // data为list数据 // 在这里还可以为list排序 return data.sort();
   }).then((list) => {
       // 好了,这里的拿到list 可以使用了
   })