AJAX与Promise简单使用

2,821 阅读5分钟

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()

简略版:

var request = new XMLHttpRequest()
request.open('GET','/xxx')
request.onload = ()=>{console.log('请求成功')
request.send()

3. onreadystatechange

状态描述
0UNSENT代理被创建,但尚未调用 open() 方法。
1OPENEDopen() 方法已经被调用。
2HEADERS_RECEIVEDsend() 方法已经被调用,并且头部和状态已经可获得。
3LOADING下载中; responseText 属性已经包含部分数据。
4DONE下载操作已完成。

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

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

终止请求

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

Promise

1. 介绍

Promise是在ES6中确定的对异步及回调处理的规范实现。

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

  • 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled): 意味着操作成功完成。
  • 已拒绝(rejected): 意味着操作失败。

2. 使用Promise封装ajax

关键:return new Promise((resolve, reject)=>{...}) 用以返回一个Promise对象。

ajax = (method, url) => {
    return new Promise((resolve, reject)=>{ //关键,用以返回一个Promise对象
        var request = new XMLHttpRequest() 
        request.open(method, url)
        request.onreadystateChange = function(
            if(request.readyState === 4){
                console.log('请求完成')
                if(request.responese.status === 200){
                    console.log('请求成功')
                    resolve.call(null, request.response) //成功则调用resolve
                }else{
                    console.log('请求失败')
                    reject.call(null, request) //失败则调用reject
                }
            }
        )
        request.send()
    })
}

3. then / catch / finally 函数

一个Promise对象具有then函数,then函数接受两个函数,第一个函数当 Promise 变成接受状态(fulfilled)时被调用,第二个函数当 Promise 变成拒绝状态(rejected)时被调用。

catch 接收一个函数,在Promise被reject的时候执行,除非该函数抛出错误或返回一个失败的Promise,否则返回的Promise一直是resolved。实质上catch(failureCallback) 是 then(null, failureCallback) 的缩略形式。

finally接收一个回调函数,在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定该回调函数。这避免了同样的语句需要在then()和catch()中各写一次的情况。

以调用上面封装的ajax函数为例

ajax('GET','/user')
.then(
    res=> console.log(res),
    request => console.log(request)
)
.catch(err => console.log(err))
.finally( () => console.log('结束') )

4. all / allSettled / race / any / 函数

  • 对比小结:

    • 都接受一个可迭代对象
    • all:全resolve才resolve,任何一个reject则reject
    • allSettled: 不关心结果,只关心是否全部已结束
    • race:任何一个resolve或reject,则返回其状态
    • any:任何一个resolve,则成功。全为reject,则reject。
  • Promise.all(iterable)

    返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

    const promise1 = Promise.resolve(3);
    const promise2 = 42;
    const promise3 = new Promise((resolve, reject) => {
      setTimeout(resolve, 100, 'foo');
    });
    
    Promise.all([promise1, promise2, promise3]).then((values) => {
      console.log(values);
    });
    // expected output: Array [3, 42, "foo"]
    
  • Promise.allSettled(iterable)

    返回一个Promise实例,当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。

const promise1 = Promise.resolve(1);
const promise2 = Promise.reject(2);

Promise.allSettled([promise1, promise2]).then((results) => {
    console.log(results);
})
// [
//   {
//        "status": "fulfilled",
//        "value": 1
//    },
//    {
//        "status": "rejected",
//        "reason": 2
//    }
//]

results的每个成员是一个对象,对象的格式是固定的,对应异步操作的结果

// 异步操作成功时
{status: 'fulfilled', value: value}

// 异步操作失败时
{status: 'rejected', reason: reason}
  • Promise.race(iterable)

    当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。

    const promise1 = new Promise((resolve, reject) => {
      setTimeout(resolve, 500, 'one');
    });
    
    const promise2 = new Promise((resolve, reject) => {
      setTimeout(resolve, 100, 'two');
    });
    
    Promise.race([promise1, promise2]).then((value) => {
      console.log(value);
      // Both resolve, but promise2 is faster
    });
    // expected output: "two"
    
  • Promise.any(iterable)

    接收一个Promise对象的集合,当其中的一个 promise 成功,就返回那个成功的promise的值。

    const pErr = new Promise((resolve, reject) => {
      reject("总是失败");
    });
    
    const pSlow = new Promise((resolve, reject) => {
      setTimeout(resolve, 500, "最终完成");
    });
    
    const pFast = new Promise((resolve, reject) => {
      setTimeout(resolve, 100, "很快完成");
    });
    
    Promise.any([pErr, pSlow, pFast]).then((value) => {
      console.log(value);
      // pFast fulfils first
    })
    // 期望输出: "很快完成"
    

async / await

  1. 介绍
asyncawait关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。

async声明一个函数为异步执行的,await则是等待一个异步方法执行完成。

async函数一定会返回一个promise对象。如果一个async函数的返回值看起来不是promise,那么它将会被隐式地包装在一个promise中。

await后面的代码可近似认为是在promise的then的回调的,多个await相当于链式调用多个then。

使用async / await,则无需使用promise.then传递回调函数。在await之后的代码会等待结束后继续执行,错误处理使用try...catch..

以下代码实现的功能等价:

  • 使用Promise
ajax('GET','user').then((res)=>console.log(res), (err)=>console.log(err))
  • 使用async / await
async xxx(){
    try{
        let result = await ajax('GET','user')
        console.log(result)
    }catch(err){
        console.log(error)
    }
}
xxx()