Promise再回首-- 再深入点了解异步编程

415 阅读5分钟

菲菲古装
之前写代码时,一般处理异步都是async-await一把梭,相比回调处理会舒服很多,用得时候如鱼得水, 但是真要深究其原理就迷迷糊糊了。 所以今天需要去探寻一下Promise的真谛(async-await本质上是Promise的语法糖)。

为什么要使用Promise

想一想,我们之前是怎么处理异步编程的(回调函数和事件),基于回调函数的异步编程通常代码冗杂,层层嵌套,不易于维护,甚至可能造成‘回调地狱’的惨状。
Promise则是另外一种异步编程的方案,它比回调函数更优雅美观,也更易于维护。起初有各大社区实现Promise的功能,到ES6的时候,TC39将Promise方案写进了标准之中,统一实现,在原生的层面上实现了Promise。

怎么去理解Promise

这边参照阮大大的想法,我们可以把Promise理解成一个容器,容器会保存异步操作(比如http请求或者定时器setTimeout)的结果(resolved或rejected),我们可以通过Promise的相关方法获取异步操作的完成结果或者捕获其错误。

Promise一般有一下三种状态可能:pendingfullfilled(resolved)rejected,以及两种状态过渡: fullfilled 到 resolvedfullfilled到rejected。所以promise对象一般有两种结果状态:1. resolved(完成),2.rejected(失败),而且Promise的结果状态是确定之后就无法改变的。

基本用法

promise实例

promise对象由Promise构造函数实例后生成。

const pro = new Promise( (resolve,reject) => {
   // xhr 请求
   // 请求成功后 resolve 结果
   // 请求遇到问题 reject 错误信息
})

Promsie构造器接收一个带有resolve和reject两个参数(也是函数)的函数(通常里面包含了异步操作)。实例化构造函数时,会直接调用该函数,异步操作也将会被触发。

当异步操作取得结果时,我们可以resolve这个promise实例,将promise的状态从fullfilled转变为resolved,并将结果作为参数传递给promise实例。 同样如果异步操作发生错误时,我们可以reject这个promise实例,将promise的状态从fullfilled转为rejected状态,并将错误信息作为参数传递给promise实例。

promise实例的then与catch方法

promise对象提供了thencatch方法(Promise原型链上的方法),方便我们对promise状态发生改变时,对所接受到的信息进行处理。

pro.then( res => {
   // 这里的res就是resolve该promise时所传递的数据
    console.log(res);
})
.catch(error => {
   // 这里的error就是reject该promise时所捕获的错误信息
   console.log(error);
   
})

then方法需要传递一个函数,当promise状态变为resolved时,将会调用该函数,并将传递的数据作为参数。同理promise状态变为rejected时,catch的方法会被调用,且promise中捕获的错误将会被作为参数。

注意

then方法返回的是一个promise对象,具体返回的promise取决于then方法中的回调函数的返回值。因为这种机制,所以promise可以链式调用then方法,也就可以将依赖前一个异步结果的then方法返回,然后继续处理。 then方法的返回依据参考 mdn文档 then方法返回值

注意

catch方法会捕获其之前在promise链式调用时抛出的错误,而不仅仅只是promise本身抛出的错误。 还有一点,promise内部的错误如果不被reject,错误不会被显性得抛出,catch也得不到错误信息。

catch方法会返回一个promise,所以其后面也可以使用链式调用。

pro
.then(res => {
  // 这里的res就是resolve该promise时所传递的数据
  console.log(res);
  const pro1 = new Promise((resolve, reject) => {
    // 一些 xhr 请求
    // 请求成功后 resolve
    // 请求遇到问题 reject
  });
  return pro1
})
.then(res1 => {
  // 这里处理 pro1成功处理的相关数据
  console.log(res1);
})
.catch(error => {
  // 这里的error就是promise链式调用时所抛出的错误信息
  console.log(error);
});

Promise构造器本身方法

Promise.all

const p = Promise.all([p1, p2, p3]);

all方法需要传入一组promise实例作为参数,all方法本身会返回一个promise实例(p),p的状态取决于传入的promise组。

  1. 如果传入的promise组中所有的promise都是resolved状态,那么p的状态也将变为resolved,此时promise组的返回值合并为数组传递给p
  2. 如果传入的promise组中有一个promise为rejected状态时,那么p的状态将变为rejected,此时rejected抛出的错误将传递给p

Promise.race

const p = Promise.race([p1, p2, p3]);

与all相似,需要传入promise组,但是p的状态改变模式不太一样。 promise组中任意一个promise状态变为resolved时,race方法返回的promise将会把状态改为resolved。

Promise.allSettled(兼容性较差)

const p = Promise.allSettled([p1, p2, p3]);

与all相似,需要传入promise组,但是p的状态改变模式也不太一样。 p将会在promise组中所有promise从fullfilled状态转变为rejected或resolved时,转变为resolved转态,promise组返回值或者抛出的错误以数组的方式传递给p。

Promise.resolve()

const p = Promise.resolve();

简单来说,resolve方法返回是promise对象,具体需要看传入的参数是什么

  1. 如果传入是一个promise,则返回这个promise
  2. 如果传入的不是promise也不是thenable对象,就返回状态为resolved的,解析值为传入参数的promise对象
  3. 如果什么都不传入,就返回一个状态为resolved的promise对象
  4. 还有一个thenable对象情况

Promise.reject()

const p = Promise.reject();

reject方法会返回一个状态为rejected的promise对象,reject值为方法传入的参数。

最后

 好了,到这里已经把Promise比较基础的部分都梳理了一遍,如果有不懂或者有问题的部分可以评论!