天天看点

promise的状态以及api介绍_如何实现一个 promise

本文转载于 SegmentFault 社区

社区专栏:chlorine

作者:chlorine

promise的状态以及api介绍_如何实现一个 promise

promise 是 ES6 中新增的一种异步解决方案,在日常开发中也经常能看见它的身影,例如原生的 fetch API 就是基于 promise 实现的。那么 promise 有哪些特性,如何实现一个具有 promise/A+ 规范的 promise 呢?

promise 特性

首先我们整理一下 promise 的一些基本特性和 API,完整的 promise/A+ 规范可以参考【翻译】Promises/A+规范

•  状态机

  • 具有 pending、fulfilled、rejected 三个状态
  • 只能由 pending -> fulfilled 和 pending -> rejected 这两种状态变化,且一经改变之后状态不可再变
  • 成功时必须有一个不可改变的值 value,失败时必须有一个不可改变的拒因 reason

•  构造函数

  • Promise 接受一个函数作为参数,函数拥有两个参数 fulfill 和 reject
  • fulfill 将 promise 状态从 pending 置为 fulfilled,返回操作的结果
  • reject 将 promise 状态从 pending 置为 rejected,返回产生的错误

•  then 方法

  • 接受两个参数 onFulfilled 和 onRejected,分别表示 promise 成功和失败的回调
  • 返回值会作为参数传递到下一个 then 方法的参数中

•  异步处理

•  链式调用

•  其他 API

  • catch、finally
  • resolve、reject、race、all 等

实现

接下来我们逐步实现一个具有 promise/A+ 规范的 promise

基本实现

先定义一个常量,表示 promise 的三个状态  

然后在 promise 中初始化两个参数 value 和 reason ,分别表示状态为 fulfill 和 reject 时的值,接着定义两个函数,函数内部更新状态以及相应的字段值,分别在成功和失败的时候执行,然后将这两个函数传入构造函数的函数参数中,如下:

接下来初步实现一个 then 方法,当当前状态是 fulfulled 时,执行成功回调,当前状态为 rejected 时,执行失败回调:  

这个时候一个简单的 MyPromise 就实现了,但是此时它还只能处理同步任务,对于异步操作却无能为力。

异步处理

要想处理异步操作,可以利用队列的特性,将回调函数先缓存起来,等到异步操作的结果返回之后,再去执行相应的回调函数。 具体实现来看,在 then 方法中增加判断,若为 pending 状态,将传入的函数写入对应的回调函数队列;在初始化 promise 时利用两个数组分别保存成功和失败的回调函数队列,并在 fulfill 和 reject 回调中增加它们。如下:

链式调用

接下来对 MyPromise 进行进一步改造,使其能够支持链式调用,使用过 jquery 等库应该对于链式调用非常熟悉,它的原理就是调用者返回它本身,在这里的话就是要让 then 方法返回一个 promise 即可,还有一点就是对于返回值的传递: 实现到这一步的 MyPromise 已经可以支持异步操作、链式调用、传递返回值,算是一个简易版的 promise ,一般来说面试时需要手写一个 promise 时,到这个程度就足够了,完整实现 promise/A+ 规范在面试这样一个较短的时间内也不太现实。

到这一步的完整代码可以参考  promise3.js: https://github.com/lvqq/Demos/blob/master/promise/src/promise3.js

promise/A+ 规范

promise/A+ 规范中规定, onFulfilled/onRejected 返回一个值 x ,对 x 需要作以下处理: •  如果 x 与 then 方法返回的 promise 相等,抛出一个  TypeError  错误 •  如果 x 是一个  Promise  ,则保持 then 方法返回的 promise 的值与 x 的值一致 •  如果 x 是对象或函数,则将  x.then  赋值给  then  并调用

  • 如果 then 是一个函数,则将 x 作为作用域 this 调用,并传递两个参数 resolvePromise 和 rejectPromise,如果 resolvePromise 和 rejectPromise 均被调用或者被调用多次,则采用首次调用并忽略剩余调用
  • 如果调用 then 方法出错,则以抛出的错误 e 为拒因拒绝 promise
  • 如果 then 不是函数,则以 x 为参数执行 promise

•  如果 x 是其他值,则以 x 为参数执行 promise 接下来对上一步实现的 MyPromise 进行进一步优化,使其符合 promise/A+ 规范:  

这里将处理返回值 x 的行为封装成为了一个函数 generatePromise ,实现如下:

promise/A+ 规范中还规定,对于  promise2 = promise1.then(onFulfilled, onRejected) •  onFulfilled/onRejected 必须异步调用,不能同步 •  如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值 •  如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的拒因 对于 then 方法做最后的完善,增加 setTimeout 模拟异步调用,增加对于 onFulfilled 和 onRejected 方法的判断: 实现 promise/A+ 规范的 promise 完整代码可以参考  promise4.js 如何知道你实现的 promise 是否遵循 promise/A+ 规范呢?可以利用 promises-aplus-tests 这样一个 npm 包来进行相应测试。

其他 API

这里对其他常用的 promise API 进行了实现

1. catch、finally

2. Promise.resolve

返回一个 resolved 状态的 Promise 对象

3. Promise.reject

返回一个 rejected 状态的 Promise 对象

4. Promise.race

返回一个 promise ,一旦迭代器中的某个 promise 状态改变,返回的 promise 状态随之改变

5. Promise.all

返回一个 promise ,只有迭代器中的所有的 promise 均变为 fulfilled ,返回的 promise 才变为 fulfilled ,迭代器中出现一个 rejected ,返回的 promise 变为 rejected

6. Promise.allSettled

只有等到迭代器中所有的 promise 都返回,才会返回一个 fulfilled 状态的 promise ,并且返回的 promise 状态总是 fulfilled ,不会返回 rejected 状态   参考 •  MDN-Promise: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise •  ECMAScript 6 入门-Promise 对象: https://es6.ruanyifeng.com/#docs/promise •  【翻译】Promises/A+规范: https://www.ituring.com.cn/article/66566 •  完整的示例和测试代码: github/lvqq/promise - END -

promise的状态以及api介绍_如何实现一个 promise