「小记」深入了解 Promise 原理

48 阅读11分钟

一、跟着 Promises/A+ 手写 Promise

1. 实现 Promise 的基本功能

1.1 代码:

// promise01.js
// Promise 的基本用法(executor、then 方法)
/**
 * new Promise((resolve,reject)=>{})
 * ① Promise 传入一个 executor 立即执行函数;
 * ② executor 接收两个参数:resolve 函数和 reject 函数;
 * Promise 有三个状态:pending、fulfilled、rejected;
 * ① 状态只能从 pending => fulfilled,pending => rejected;
 * ② 状态一旦确认,就不会再改变;
 * Promise 都有 then 方法
 * ① 接收两个可选参数:成功的回调 onFulfilled、失败的回调 onRejected;
 * ② then 可以在同一个 Promise 上多次调用;
 */
const Pending = 'pending'
const Fulfilled = 'fulfilled'
const Rejected = 'rejected'
class MyPromise {
  constructor(executor) { // promise 传入一个 executor 立即执行函数
    // 定义在实例上的属性和方法,实例独有
    this.state = Pending // 状态
    this.value = undefined // 任何合法的js值(undefined、thenable:定义then方法的对象或函数、promise)
    this.reason = undefined // 一个表示 promise 被reject 的原因
    const resolve = (value) => {
      if (this.state === Pending) {
        this.state = Fulfilled
        this.value = value
      }
    }
    const reject = (reason) => {
      if (this.state === Pending) {
        this.state = Rejected
        this.reason = reason
      }
    }

    try { // 执行器抛出错误
      executor(resolve, reject)// executor 有两个入参:resolve 函数和 reject 函数
    } catch (e) { // 捕获异常,直接reject
      reject(e)
    }
  }
  // 定义在原型 prototype 上的方法或属性,所有实例共享(继承同一个)
  then(onFulfilled, onRejected) {
    if (this.state === Fulfilled) {
      onFulfilled(this.value)
    }
    if (this.state === Rejected) {
      onRejected(this.reason)
    }
  }
}
module.exports = MyPromise

1.2 测试代码

// ① Promise 的基本用法(executor、then 方法)
const MyPromise = require('./promise01')
const p = new MyPromise((resolve, reject) => {
  // resolve('success') // ①
  reject('filed') // ②
  // throw Error('executor 中抛出错误') // ③
})
p.then((value) => {
  console.log('fulfilled1:', value) // 执行① fulfilled1: success
}, (reason) => {
  console.log('rejected1:', reason)
  // 执行② rejected1: filed
  // 执行③ rejected1: Error: executor 中抛出错误
})
p.then((value) => {
  console.log('fulfilled2:', value) // fulfilled2: success
}, (reason) => {
  console.log('rejected2:', reason)
  // 执行② rejected2: filed
  // 执行③ rejected2: Error: executor 中抛出错误
})

2. 实现 Promise 的异步调用

2.1 代码

// promise02.js
/**
 * Promise 实现异步调用
 * new Promise((resolve,reject)=>{})
 * ① Promise 传入一个 executor 立即执行函数;
 * ② executor 接收两个参数:resolve 函数和 reject 函数;
 * Promise 有三个状态:pending、fulfilled、rejected;
 * ① 状态只能从 pending => fulfilled,pending => rejected;
 * ② 状态一旦确认,就不会再改变;
 * Promise 都有 then 方法
 * ① 接收两个可选参数:成功的回调 onFulfilled、失败的回调 onRejected;
 * ② then 可以在同一个 Promise 上多次调用;
 * Promise 的异步调用(发布订阅模式)
 * ① 在 then 方法中订阅(收集成功或失败的回调)
 * ② 在 resolve 方法中发布成功的回调(依次执行成功地回调函数)
 * ③ 在  reject 方法中发布失败的回调(依次执行失败的回调函数)
 */
const Pending = 'pending'
const Fulfilled = 'fulfilled'
const Rejected = 'rejected'
class MyPromise {
  constructor(executor) { // promise 传入一个 executor 立即执行函数
    // 定义在实例上的属性和方法,实例独有
    this.state = Pending // 状态
    this.value = undefined // 任何合法的js值(undefined、thenable:定义then方法的对象或函数、promise)
    this.reason = undefined // 一个表示 promise 被reject 的原因
    this.onFulfilledCallbacks = [] // 存成功回调的数组
    this.onRejectedCallbacks = [] // 存失败回调的数组

    const resolve = (value) => {
      if (this.state === Pending) {
        this.state = Fulfilled
        this.value = value
        // 异步的情况发布订阅模式
        // 发布:执行成功的回调
        this.onFulfilledCallbacks.forEach(fn => fn())
      }
    }
    const reject = (reason) => {
      if (this.state === Pending) {
        this.state = Rejected
        this.reason = reason
        // 异步的情况发布订阅模式
        // 发布:执行失败的回调
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    try { // 执行器抛出错误
      executor(resolve, reject)// executor 有两个入参:resolve 函数和 reject 函数
    } catch (e) { // 捕获异常,直接reject
      reject(e)
    }
  }
  // 定义在原型 prototype 上的方法或属性,所有实例共享(继承同一个)
  then(onFulfilled, onRejected) {
    if (this.state === Fulfilled) {
      onFulfilled(this.value)
    }
    if (this.state === Rejected) {
      onRejected(this.reason)
    }
    // 异步的情况发布订阅模式
    // 订阅:收集成功或失败的回调
    if (this.state === Pending) {
      this.onFulfilledCallbacks.push(() => {
        onFulfilled(this.value)
      })
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason)
      })
    }
  }
}
module.exports = MyPromise

2.2 测试代码

// ② Promise 的异步调用
const MyPromise = require('./promise02')
const p = new MyPromise((resolve, reject) => {
  // 非异步
  // resolve('success')
  // reject('filed')
  // 异步
  setTimeout(() => {
    resolve('success')
    // reject('filed')
  }, 2000)
})
p.then((value) => {
  console.log('fulfilled1:', value)
}, (reason) => {
  console.log('rejected1:', reason)
})
p.then((value) => {
  console.log('fulfilled2:', value)
}, (reason) => {
  console.log('rejected2:', reason)
})
// resolve('success')
// 2秒后打印:
// fulfilled1: success
// fulfilled2: success

// reject('filed')
// 2秒后打印:
// rejected1: filed
// rejected2: filed

3. 实现 Promise 的链式调用

3.1 代码

// promise.js
/**
 * new Promise((resolve,reject)=>{})
 * ① Promise 传入一个 executor 立即执行函数;
 * ② executor 接收两个参数:resolve 函数和 reject 函数;
 *
 * Promise 有三个状态:pending、fulfilled、rejected;( Promises/A+ 2.1)
 * ① 状态只能从 pending => fulfilled,pending => rejected;( Promises/A+ 2.1.1)
 * ② 状态一旦确认,就不会再改变;(Promises/A+ 2.1.2,Promises/A+ 2.1.3)
 *
 * Promise 都有 then 方法(Promises/A+ 2.2);
 * ① 接收两个可选函数形参:成功的回调 onFulfilled、失败的回调 onRejected;(Promises/A+ 2.2.1)
 * ② onFulfilled 和 onRejected 都是 function;( Promises/A+ 2.2.2, Promises/A+ 2.2.3)
 * ③ onFulfilled 和 onRejected 要等到同步代码执行完后再执行;( Promises/A+ 2.2.4)
 * ④ then 可以在同一个 Promise 上多次调用;( Promises/A+ 2.2.6)
 * Promise 的异步调用(发布订阅模式)
 * ① 在 then 方法中订阅(收集成功或失败的回调)
 * ② 在 resolve 方法中发布成功的回调(依次执行成功地回调函数)
 * ③ 在  reject 方法中发布失败的回调(依次执行失败的回调函数)
 *
 * Promise 的链式调用
 * ① then 必须返回一个 Promise:promise2 = promise1.then(onFulfilled,onRejected);( Promises/A+ 2.2.7)
 * ①-① 定义 resolvePromise(promise2,x) 方法处理成功回调(onFulfilled)或失败回调(onRejected)返回的值 x;( Promises/A+ 2.2.7.1)
 * ①-② 成功回调(onFulfilled)或失败回调(onRejected)报错,抛出错误的原因 reason;( Promises/A+ 2.2.7.2)
 * ①-③ onFulfilled 不是 function ,直接返回 value 值;( Promises/A+ 2.2.7.3 )
 * ①-④ onRejected 不是 function ,抛出 reason;( Promises/A+ 2.2.7.4 )
 *
 * ② resolvePromise(promise2,x);( Promises/A+ 2.3)
 * ②-① promise2 和 x 是相同的引用,抛出 TypeError;( Promises/A+ 2.3.1)
 * ②-② x 是 promise ,延用 x 的状态;( Promises/A+ 2.3.2)
 * ②-③ x 是 object 或 function ;( Promises/A+ 2.3.3)
 * ②-④ x 不是 object 或 function:resolve(x);( Promises/A+ 2.3.4)
 */
function resolvePromise(promise2, x, resolve, reject) {
  /* promise2 和 x 是相同的引用,抛出 TypeError;( Promises/A+ 2.3.1)*/
  if (promise2 === x) {
    return reject(new TypeError('死循环了'))
  }
  /* x 是 object(非null)或 function;( Promises/A+ 2.3.3 )*/
  if ((x && typeof x === 'object') || typeof x === 'function') {
    // 只能调用一次(PromiseA+ 2.3.3.3.3);
    let called = false
    try { // PromiseA+ 2.3.3.2
      /* PromiseA+ 2.3.3.1 */
      const then = x.then
      /* PromiseA+ 2.3.3.3;
       * then 是 function;
       * 调用 then ,x 作 this 传入,两个形参:resolvePromise、rejectPromise;
       */
      if (typeof then === 'function') {
        then.call(x, (y) => { // PromiseA+ 2.3.3.3.1
          if (called) return
          called = true
          resolvePromise(promise2, y, resolve, reject)
        }, (r) => { // PromiseA+ 2.3.3.3.2
          if (called) return
          called = true
          reject(r)
        })
      } else { // PromiseA+ 2.3.3.4
        if (called) return
        called = true
        resolve(x)
      }
    } catch (e) { // PromiseA+ 2.3.3.2
      if (called) return
      called = true
      reject(e)
    }
  } else { // x 不是 object 或 function ,返回 x 的值;( Promises/A+ 2.3.4 )
    resolve(x)
  }
}
/* Promise 有三种状态:pending、fulfilled、rejected;( Promises/A+ 2.1)*/
const Pending = 'pending'
const Fulfilled = 'fulfilled'
const Rejected = 'rejected'
class MyPromise {
  constructor(executor) { // promise 传入一个 executor 立即执行函数
    // 定义在实例上的属性和方法,实例独有
    this.state = Pending // 状态
    this.value = undefined // 任何合法的js值(undefined、thenable:定义then方法的对象或函数、promise)
    this.reason = undefined // 一个表示 promise 被reject 的原因

    // then 被调用多次,存在多个成功或失败的回调函数( Promises/A+ 2.2.6)
    this.onFulfilledCallbacks = [] // 存成功回调的数组
    this.onRejectedCallbacks = [] // 存失败回调的数组

    const resolve = (value) => {
      if (this.state === Pending) {
        this.state = Fulfilled
        this.value = value
        // 异步的情况发布订阅模式
        // 发布:执行成功的回调
        this.onFulfilledCallbacks.forEach(fn => fn()) // ( Promises/A+ 2.2.6.1)
      }
    }
    const reject = (reason) => {
      if (this.state === Pending) {
        this.state = Rejected
        this.reason = reason
        // 异步的情况发布订阅模式
        // 发布:执行失败的回调
        this.onRejectedCallbacks.forEach(fn => fn()) // ( Promises/A+ 2.2.6.2)
      }
    }
    try { // 执行器抛出错误
      executor(resolve, reject)// executor 有两个入参:resolve 函数和 reject 函数
    } catch (e) { // 捕获异常,直接reject
      reject(e)
    }
  }
  // 定义在原型 prototype 上的方法或属性,所有实例共享(继承同一个)
  /**
   * then 方法
   * 可选的两个形参;( Promises/A+ 2.2.1 )
   * 形参作为 function 被调用;( Promises/A+ 2.2.5 )
   */
  then(onFulfilled, onRejected) {
    /**
     * onFulfilled 不是 function ,直接返回 value 值;( Promises/A+ 2.2.7.3 )
     * onRejected 不是 function ,抛出 reason;( Promises/A+ 2.2.7.4 )
     */
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

    // 定义 promise2 接收 then 方法返回的 promise;
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.state === Fulfilled) {
        setTimeout(() => { //  模拟同步代码执行后再执行; Promises/A+ 2.2.4)
          try { // onFulfilled 报错,抛出异常原因;( Promises/A+ 2.2.7.2)
            const x = onFulfilled(this.value) // onFulfilled 返回 x 值;( Promises/A+ 2.2.7.1)
            resolvePromise(promise2, x, resolve, reject) // 处理 x 的值;( Promises/A+ 2.3)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      if (this.state === Rejected) {
        setTimeout(() => { // 模拟同步代码执行后再执行;( Promises/A+ 2.2.4)
          try { // onRejected 报错,抛出异常原因;( Promises/A+ 2.2.7.2)
            const x = onRejected(this.reason) // onRejected 返回 x 值;( Promises/A+ 2.2.7.1)
            resolvePromise(promise2, x, resolve, reject) // 处理 x 的值;( Promises/A+ 2.3)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      // 异步的情况发布订阅模式
      // 订阅:收集成功或失败的回调
      if (this.state === Pending) {
        this.onFulfilledCallbacks.push(() => {
          try {
            const x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
        this.onRejectedCallbacks.push(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
    })
    return promise2 // then 方法返回一个 promise( Promises/A+ 2.2.7)
  }
}
module.exports = MyPromise

3.2 测试代码

const MyPromise = require('./promise')
const p1 = new MyPromise((resolve, reject) => {
  resolve('p1')
})
const p2 = p1.then(() => {
  // return new Error('error') // resolve: Error: error
  // return Promise.resolve('promise.resolve') // resolve: promise.resolve
  return new MyPromise((resolve, reject) => {
    // resolve('p2') // resolve: p2
    setTimeout(() => {
      // resolve('MyPromise') // 2秒后 resolve: MyPromise
      resolve(new MyPromise((resolve, reject) => {
        resolve('多层嵌套返回 resolve') // 2 秒后 resolve: 多层嵌套返回 resolve
      }))
    }, 2000)
  })
})
p2.then((value) => {
  console.log('resolve:', value)
}, (value) => {
  console.log('reject:', value)
})

4. 实现 Promise 其他方法

// catch、finally、resolve、reject、all
class MyPromise {
  constructor(executor) { }
  then(onFulfilled, onRejected) {}
  
  catch(errorCallBack) {
    return this.then(null, errorCallBack)
  }
  finally(callBack) {
    return this.then(callBack, callBack)
  }
  // x 是 promise ,直接返回;
  // x 如果是 thenable,跟随 thenable 对象的状态;
  // 否则,直接 resolve(x)
  static resolve(x) {
    // x 是 promise ,返回 x;
    if (x instanceof MyPromise) {
      return x
    }
    return new MyPromise((resolve, reject) => {
      if (x && x.then && typeof x.then === 'function') {
        const then = x.then
        setTimeout(() => {
          then.call(x, resolve, reject)
        }, 0)
      } else {
        resolve(x)
      }
    })
  }
  // 直接返回 reject 理由,变成后续方法的参数
  static reject(r) {
    return new MyPromise(reject => {
      reject(r)
    })
  }
  static all(iterable) {
    return new MyPromise((resolve, reject) => {
      if (Array.isArray(iterable)) {
        const result = [] // 存储结果
        let count = 0 // 计数器
        // 长度为0 ,返回一个已经完成状态的 promise
        if (iterable.length === 0) {
          return resolve(iterable)
        }
        iterable.forEach((item, index) => {
          if (item instanceof MyPromise) {
            MyPromise.resolve(item).then(value => {
              count++
              result[index] = value
              count === iterable.length && resolve(result)
            }, reason => {
              // 存在一个失败,直接返回失败的原因
              reject(reason)
            })
          } else {
            count++
            result[index] = item // 不是 promise ,原样返回
            count === iterable.length && resolve(result)
          }
        })
      } else {
        return reject(new TypeError('Argument is not iterable'))
      }
    })
  }
}
// MyPromise.resolve = (x) => {}
// MyPromise.reject = (r) => {}
module.exports = MyPromise

5. 参考链接

  1. 全网首发:你不知道的『Promise』
  2. Promises/A+

二、原生 Promise 的一些'奇怪'现象

1. 例子一

1.1 打印什么?

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(4);
}).then(() =>{
    console.log(5);
})
Promise.resolve().then(() => {
    console.log(6);
}).then(() => {
    console.log(7);
}).then(() => {
    console.log(8);
}).then(() => {
    console.log(9);
}).then(() =>{
    console.log(10);
})

1.2 思路&结果

注意:Js引擎为了让 microtask 尽快的输出,做了一些优化,连续的多个then(3个)如果没有 reject 或者 resolve 会交替执行 then 而不至于让一个堵太久(V8源码有体现) 。

① 微任务队列【1, 2, 3, 4, 5】
② 微任务队列【6, 7, 8, 9, 10】
①②交替执行;

结果:1、6、2、7、3、8、4、9、5、10

2. 例子二

2.1 打印什么?

Promise.resolve().then(() => {
    console.log(1);
    return Promise.resolve(2);
}).then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(3);
}).then(() => {
    console.log(4);
}).then(() => {
    console.log(5);
}).then(() => {
    console.log(6);
}).then(() =>{
    console.log(7);
})

2.2 思路&结果

注意:在 chrome 内部实现的 Promise 和标准的 Promise/A+ 规范存在差异。

浏览器内部实现的区别:我们可以理解为,resolve 或者 return 一个 Promise 对象:

  • 当发现 Promise.resolve() 时,会创建一个微任务(核心原理相当于调用 then 来处理一些逻辑);
  • 当处理 Promise.resolve() 时,会创建一个微任务(参考手写的 resolvePromise(promise2,x) 方法,当处理 x 是 Promise 时,调用了一次 then 方法 );
Promise.resolve().then(() => {
    console.log(1);
    return Promise.resolve(2); 
}).then((res) => {
    console.log(res)
})
// 上面的代码可以理解为:
Promise.resolve().then(() => {
    console.log(1);
    return 2
})
.then()
.then()
.then((res) => {
    console.log(res);
})

同理

new Promise(resolve=>{
    resolve(Promise.resolve(1))
}).then((res) => { console.log(res) })

// 可以理解为:
new Promise(resolve => { 
    resolve(1); 
})
.then()
.then()
.then((res) => { console.log(res) })

结合例子一和例子二:
① 微任务队列【1, , , 2】
② 微任务队列【3, 4, 5, 6, 7】
①②交替执行;

结果:1、3、4、5、2、6、7

3. 例子三

3.1 打印什么?

new Promise((resolve, reject) => {
    console.log(1);
    resolve();
}).then(() => {
    console.log(2);
    new Promise((resolve, reject) => {
        console.log(3);
        resolve();
    }).then(() => {
        console.log(4);
    }).then(() => {
        console.log(5);
    });
    return new Promise((resolve, reject) => {
        console.log(6);
        resolve();
    }).then(() => {
        console.log(7);
    }).then(() => {
        console.log(8);
    });
  }).then(() => { console.log(9); });

3.2 思路&结果

new Promise((resolve, reject) => {
    console.log(1);
    resolve();
}).then(() => {
    console.log(2);
    new Promise((resolve, reject) => {
        console.log(3);
        resolve();
    }).then(() => {
        console.log(4);
    }).then(() => {
        console.log(5);
    });
    // 下一个 then 必须等 return 的值
    return new Promise((resolve, reject) => {
        console.log(6);
        resolve();
    }).then(() => {
        console.log(7);
    }).then(() => {
        console.log(8);
    });
  }).then(() => { console.log(9); });

① 处理宏任务,立即打印1,待处理的微任务队列【then、9】;
② 处理微任务,立即打印2、3、6,待处理的微任务队列【4、5】【7、8】、【9】;
③ 微任务【4、5】【7、8】交替打印4、7、5、8,9 必须等 8 执行完;

结果:1、2、3、6、4、7、5、8、9

4. 例子四

4.1 打印什么?

//第一段代码
new Promise((resolve, reject) => {
    console.log(1);
    resolve();
}).then(() => {
    console.log(2);
    return new Promise((resolve, reject) => {
        console.log(3);
        resolve();
    }).then(() => {
        console.log(4);
    }).then(() => {
        console.log(5);
    });
}).then(() => { console.log(6); });

// 第二段代码
new Promise((resolve, reject) => {
    console.log(11);
    resolve();
}).then(() => {
    console.log(22);
    new Promise((resolve, reject) => {
        console.log(33);
        resolve();
    }).then(() => { 
        console.log(44); 
    }).then(() => { 
        console.log(55); 
    });
}).then(() => { console.log(66); });

// 第三段代码
new Promise((resolve, reject) => {
    console.log(111);
    resolve();
}).then(() => {
    console.log(222);
    let p = new Promise((resolve, reject) => {
        console.log(333);
        resolve();
    })
    p.then(() => { console.log(444); })
    p.then(() => { console.log(555); });
}).then(() => { console.log(666); });

4.2 思路&结果

//第一段代码
new Promise((resolve, reject) => {
    console.log(1);
    resolve();
}).then(() => {
    console.log(2);
    // return 一个 Promise ,下一个 then 必须等 Promise 的结果
    return new Promise((resolve, reject) => {
        console.log(3);
        resolve();
    }).then(() => {
        console.log(4);
    }).then(() => {
        console.log(5);
    });
}).then(() => { console.log(6); });
// 第一段代码结果:1、2、3、4、5、6

// 第二段代码
new Promise((resolve, reject) => {
    console.log(11);
    resolve();
}).then(() => {
    console.log(22);
    // 少了一个 return ,下一个 then 不用等待
    new Promise((resolve, reject) => {
        console.log(33);
        resolve();
    }).then(() => { 
        console.log(44); 
    }).then(() => { 
        console.log(55); 
    });
}).then(() => { console.log(66); });
// 第二段代码的结果:11、22、33、44、66、55

// 第三段代码
new Promise((resolve, reject) => {
    console.log(111);
    resolve();
}).then(() => {
    console.log(222);
    let p = new Promise((resolve, reject) => {
        console.log(333);
        resolve();
    })
    // then 非链式调用,不用等待
    p.then(() => { console.log(444); })
    p.then(() => { console.log(555); });
}).then(() => { console.log(666); });
// 第三段代码结果:111、222、333、444、555、666

三段代码一起考虑:
① 处理宏任务:立即打印1、11、111;待处理的微任务队列【then、6】、【then、66】、【then、666】
② 依次处理then:立即打印2、3、22、33、222、333;待处理的微任务队列【【4、5】、6】、【44、55】、【66】、【444】、【555】、【666】
③ 待处理队列交替执行:4、44、66、444、555、666、5、55、6

结果:1、11、111、2、3、22、33、222、333、4、44、66、444、555、666、5、55、6

5. 参考链接

  1. 深度揭秘 Promise 微任务注册和执行过程
  2. [V8源码补充篇]从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节