手把手教你实现 Promise的使用方法

时间:2022-09-21 22:18:14

前言

很多 JavaScript 的初学者都曾感受过被回调地狱支配的恐惧,直至掌握了 Promise 语法才算解脱。虽然很多语言都早已内置了 Promise ,但是 JavaScript 中真正将其发扬光大的还是 jQuery 1.5 对 $.ajax 的重构,支持了 Promise,而且用法也和 jQuery 推崇的链式调用不谋而合。后来 ES6 出世,大家才开始进入全民 Promise 的时代,再后来 ES8 又引入了 async 语法,让 JavaScript 的异步写法更加优雅。

今天我们就一步一步来实现一个 Promise,如果你还没有用过 Promise,建议先熟悉一下 Promise 语法再来阅读本文。

构造函数

在已有的 Promise/A+ 规范 中并没有规定 promise 对象从何而来,在 jQuery 中通过调用 $.Deferred() 得到 promise 对象,ES6 中通过实例化 Promise 类得到 promise 对象。这里我们使用 ES 的语法,构造一个类,通过实例化的方式返回 promise 对象,由于 Promise 已经存在,我们暂时给这个类取名为 Deferred

  1. class Deferred {
  2. constructor(callback) {
  3. const resolve = () => {
  4. // TODO
  5. }
  6. const reject = () => {
  7. // TODO
  8. }
  9. try {
  10. callback(resolve, reject)
  11. } catch (error) {
  12. reject(error)
  13. }
  14. }
  15. }

构造函数接受一个 callback,调用 callback 的时候需传入 resolve、reject 两个方法。

Promise 的状态

Promise 一共分为三个状态:

手把手教你实现 Promise的使用方法

pending :等待中,这是 Promise 的初始状态;

手把手教你实现 Promise的使用方法

fulfilled :已结束,正常调用 resolve 的状态;

手把手教你实现 Promise的使用方法

rejected :已拒绝,内部出现错误,或者是调用 reject 之后的状态;

手把手教你实现 Promise的使用方法

我们可以看到 Promise 在运行期间有一个状态,存储在 [[PromiseState]] 中。下面我们为 Deferred 添加一个状态。

  1. //基础变量的定义
  2. const STATUS = {
  3. PENDING: 'PENDING',
  4. FULFILLED: 'FULFILLED',
  5. REJECTED: 'REJECTED'
  6. }
  7.  
  8. class Deferred {
  9. constructor(callback) {
  10. this.status = STATUS.PENDING
  11.  
  12. const resolve = () => {
  13. // TODO
  14. }
  15. const reject = () => {
  16. // TODO
  17. }
  18. try {
  19. callback(resolve, reject)
  20. } catch (error) {
  21. // 出现异常直接进行 reject
  22. reject(error)
  23. }
  24. }
  25. }

这里还有个有意思的事情,早期浏览器的实现中 fulfilled 状态是 resolved,明显与 Promise 规范不符。当然,现在已经修复了。

手把手教你实现 Promise的使用方法

内部结果

除开状态,Promise 内部还有个结果 [[PromiseResult]] ,用来暂存 resolve/reject 接受的值。

手把手教你实现 Promise的使用方法

手把手教你实现 Promise的使用方法

继续在构造函数中添加一个内部结果。

  1. class Deferred {
  2. constructor(callback) {
  3. this.value = undefined
  4. this.status = STATUS.PENDING
  5.  
  6. const resolve = value => {
  7. this.value = value
  8. // TODO
  9. }
  10. const reject = reason => {
  11. this.value = reason
  12. // TODO
  13. }
  14. try {
  15. callback(resolve, reject)
  16. } catch (error) {
  17. // 出现异常直接进行 reject
  18. reject(error)
  19. }
  20. }
  21. }

储存回调

使用 Promise 的时候,我们一般都会调用 promise 对象的 .then 方法,在 promise 状态转为 fulfilledrejected 的时候,拿到内部结果,然后做后续的处理。所以构造函数中,还需要构造两个数组,用来存储 .then 方法传入的回调。

  1. class Deferred {
  2. constructor(callback) {
  3. this.value = undefined
  4. this.status = STATUS.PENDING
  5.  
  6. this.rejectQueue = []
  7. this.resolveQueue = []
  8.  
  9. const resolve = value => {
  10. this.value = value
  11. // TODO
  12. }
  13. const reject = reason => {
  14. this.value = reason
  15. // TODO
  16. }
  17. try {
  18. callback(resolve, reject)
  19. } catch (error) {
  20. // 出现异常直接进行 reject
  21. reject(error)
  22. }
  23. }
  24. }

resolve 与 reject

 修改状态

接下来,我们需要实现 resolve 和 reject 两个方法,这两个方法在被调用的时候,会改变 promise 对象的状态。而且任意一个方法在被调用之后,另外的方法是无法被调用的。

  1. new Promise((resolve, reject) => {
  2. setTimeout(() => {
  3. resolve('