这次来搞一篇 promise 规范和基于规范手撕 Promise 的文章

什么是 Promise

Promise 表示一个异步操作的最终结果,它有三种状态,分别是:

  1. 等待态(pending)
  2. 执行态(resolved)
  3. 拒绝态(rejected)

其初始状态为 pending,在整个异步过程中,他只能处于两种状态,且一旦状态变更为另一种就不再变化

如 pending-> resolved / pending->rejected

基本过程:

  1. 初始化 Promise 状态(pending)
  2. 立即执行 Promise 中传入的 fn 函数,将Promise 内部 resolve、reject 函数作为参数传递给 fn ,按事件机制时机处理
  3. 执行 then(..) 注册回调处理数组(then 方法可被同一个 promise 调用多次)
  4. Promise里的关键是要保证,then方法传入的参数 onFulfilled 和 onRejected,必须在then方法被调用的那一轮事件循环之后的新执行栈中执行。
  1. new Promise((resolve, reject) => {
  2. resolve("success"); //调用resolve函数使得Promise状态变更为resolved
  3. reject("fail"); //此处无效 (已经变为 resolved了)
  4. });

需要注意的是:在使用 new 构造 Promise对象 的时候,构造函数内部的代码会立即执行

  1. new Promise((resolve, reject) => {
  2. console.log("first");
  3. });
  4. console.log("second");
  5. //first
  6. //second

上面这段函数执行的结果先打印first 后打印second

简而言之,Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 then 方法,该方法注册了两个回调函数,用于接收 promise 的终值或本 promise 不能执行的原因。

Promise A+ 规范

Promise 用法

  1. new Promise((resolve,reject)=>{
  2. //resolve([value]) or reject([error])
  3. }).then(
  4. res=>{
  5. },
  6. err=>{
  7. }
  8. ).then(
  9. res=>{
  10. }
  11. ,err=>{
  12. }
  13. ).catch(
  14. err => console.log(err)
  15. )
  16. Promise.resolve();
  17. Promise.reject();
  18. Promise.all([promise1,promise2,...]).then();
  19. Promise.race([promise1,promise2,...]).then();
  • Promise的构造方法接收一个executor(),在new Promise()时就立刻执行这个 executor 回调
  • executor()内部的异步任务被放入宏/微任务队列,等待执行
  • then()被执行,收集成功/失败回调,放入成功/失败队列
  • executor()的异步任务被执行,触发resolve/reject,从成功/失败队列中取出回调依次执行
  • 状态改变后触发 then、catch 的回调
  • Promise 拥有静态方法 resolve、reject、all、race

仔细一看,这不就是观察者模式:then 收集依赖 -> 异步触发 resolve -> resolve 执行依赖

根据以上写出如下框架代码

  1. class Promise {
  2. // 构造方法接收一个回调
  3. constructor(exector) {}
  4. then() {}
  5. catch() {}
  6. static resolve() {}
  7. static reject() {}
  8. static race() {}
  9. static all() {}
  10. }

首先定义三种状态

然后在构造函数中执行 exector()

  1. const PENDING = "PENDING"; // 进行中
  2. const FULFILLED = "FULFILLED"; // 已成功
  3. const REJECTED = "REJECTED"; // 已失败
  4. class Promise {
  5. constructor(exector) {
  6. this.status = PENDING; //初始化状态
  7. this.value = undefined; //准备值和错误结果
  8. this.error = undefined;
  9. const resolve = (value) => {
  10. if (this.status === PENDING) {
  11. this.status = FULFILLED;
  12. this.value = value;
  13. }
  14. };
  15. const reject = (error) => {
  16. if (this.status === PENDING) {
  17. this.status = REJECTED;
  18. this.value = error;
  19. }
  20. };
  21. exector(resolve, reject);
  22. // 立即执行exector
  23. // 把内部的resolve和reject方法传入executor,这样使用者可以调用resolve和reject方法
  24. }
  25. }

接下来实现 then 方法,then 方法可以访问其当前值终值拒因,接受两个参数onFulfilledonRejected,依据 Promise A+规范实现相关特性:

  1. const status = {
  2. pending: "PENDING",
  3. fulfiled: "FULFILED",
  4. rejected: "REJECTED",
  5. };
  6. class Promise {
  7. constructor(exector) {
  8. this.status = PENDING; //初始化状态
  9. this.value = undefined; //准备值和错误结果
  10. this.error = undefined;
  11. const resolve = (value) => {
  12. if (this.status === PENDING) {
  13. this.status = FULFILLED;
  14. this.value = value;
  15. }
  16. };
  17. const reject = (error) => {
  18. if (this.status === PENDING) {
  19. this.status = REJECTED;
  20. this.value = error;
  21. }
  22. };
  23. try {
  24. exector(resolve, reject);
  25. } catch (e) {
  26. reject(e);
  27. }
  28. // 立即执行exector
  29. // 把内部的resolve和reject方法传入executor,这样使用者可以调用resolve和reject方法
  30. }
  31. //then可以捕获exector的错误和结果,访问当前值,终值和拒因 所以需要try catch exector执行的错误
  32. then(onFulfilled, onRejected) {
  33. // then是微任务,这里用setTimeout模拟
  34. setTimeout(() => {
  35. if (this.status === FULFILLED) {
  36. onFulfilled(this.value);
  37. } else if (this.status === REJECTED) {
  38. onRejected(this.error);
  39. }
  40. });
  41. }
  42. }

测试一下:

  1. const promise = new Promise((resolve, reject) => {
  2. console.log("1");
  3. setTimeout(() => {
  4. console.log("4");
  5. });
  6. Math.random() < 0.5 ? resolve(3) : reject(-3);
  7. console.log("1.5");
  8. }).then(
  9. (res) => console.log(res),
  10. (err) => console.log(err)
  11. );
  12. console.log("2");
  13. //有结果 1 1.5 2 4 3/-3 异步调用问题
  14. console.log(promise); //undefined 还需解决链式调用问题

then参数期望是函数,传入非函数则会发生值穿透。值传透可以理解为,当传入 then 的不是函数的时候,这个 then 是无效的。

原理上是当 then 中传入的不算函数,则这个promise返回上一个promise的值,这就是发生值穿透的原因,所以只需要对then的两个参数进行设置就行了:

  1. onFulfilled =
  2. typeof onFulfilled === "function" ? onFulfilled : (value) => value;
  3. onRejected =
  4. typeof onRejected === "function"
  5. ? onRejected
  6. : (error) => {
  7. throw new Error(error instanceof Error ? error.message : error);
  8. };

开发中经常会将接口放于promise内部,等接口请求响应成功把数据resolve出去,或失败时把数据reject出去,此时thencatch才会进行捕获。

而现在的代码,promise内部如果有异步代码执行后才resolvethen不会等待异步代码执行完毕会直接执行,所以此时状态是PENDING,不会触发then的回调函数。

constructor中新增_resolveQueue_rejectQueue维护成功态、失败态任务队列

  1. // 成功态回调函数队列
  2. this._resolveQueue = [];
  3. // 失败态回调函数队列
  4. this._rejectQueue = [];
  5. const resolve = (value) => {
  6. // 只有进行中状态才能更改状态
  7. if (this.status === PENDING) {
  8. this.status = FULFILLED;
  9. this.value = value;
  10. // 新增代码:
  11. // 成功态函数依次执行
  12. this._resolveQueue.length > 0 && this._resolveQueue.forEach((fn) => fn(this.value));
  13. //while(this._resolveQueue.length > 0){
  14. // const callback = this._resolveQueue.shift();
  15. // callback(this.value)
  16. //}
  17. }
  18. };
  19. const reject = (error) => {
  20. // 只有进行中状态才能更改状态
  21. if (this.status === PENDING) {
  22. this.status = REJECTED;
  23. this.error = error;
  24. // 新增代码:
  25. // 失败态函数依次执行
  26. this._rejectQueue.length > 0 this._rejectQueue.forEach((fn) => fn(this.error));
  27. //while(this._rejectQueue.length > 0){
  28. // const callback = this._rejectQueue.shift();
  29. // callback(this.value)
  30. //}
  31. }
  32. };
  33. then(onFulfilled, onRejected) {
  34. // then是微任务,这里用setTimeout模拟
  35. setTimeout(() => {
  36. // 新增代码:
  37. if (this.status === PENDING) {
  38. // 状态是PENDING下执行
  39. // 说明promise内部有异步代码执行,还未改变状态,添加到成功/失败回调任务队列即可
  40. this._resolveQueue.push(onFulfilled);
  41. this._rejectQueue.push(onRejected);
  42. }else if (this.status === FULFILLED) {
  43. // FULFILLED状态下才执行
  44. onFulfilled(this.value);
  45. } else if (this.status === REJECTED) {
  46. // REJECTED状态下才执行
  47. onRejected(this.error);
  48. }
  49. })

Promise的一大优势就是支持链式调用,具体来说就是then方法的具体实现,实际上是返回了一个Promise,需要注意的几个点:

  1. 保存之前 promise 实例的引用,即保存this
  2. 根据then回调函数执行的返回值
  • 如果是 promise 实例,那么返回的下一个 promise 实例会等待这个 promise 状态发生变化
  • 如果不是 promise 实例,根据目前情况直接执行resolvereject
  1. then(onFulfilled, onRejected) {
  2. onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
  3. onRejected = typeof onRejected === 'function'? onRejected:
  4. error => { throw new Error(error instanceof Error ? error.message:error) }
  5. // 保存this
  6. const self = this;
  7. return new Promise((resolve, reject) => {
  8. if (self.status === PENDING) {
  9. //注意这个this指向目前的Promise对象,而不是新的Promise
  10. //再强调一遍,很重要:
  11. //目前的Promise(不是这里return的新Promise)的resolve和reject函数其实一个作为微任务
  12. //因此他们不是立即执行,而是等then调用完成后执行
  13. (this.) self._resolveQueue.push(() => {
  14. // try捕获错误
  15. try {
  16. // 模拟微任务
  17. setTimeout(() => {
  18. const result = onFulfilled(self.value);
  19. // 分两种情况:
  20. // 1. 回调函数返回值是Promise,执行then操作
  21. // 2. 如果不是Promise,调用新Promise的resolve函数
  22. //下面执行之后的下一步,也就是记录执行的状态,决定新Promise如何表现
  23. //如果返回值x是一个Promise对象,就执行then操作
  24. //如果不是Promise,直接调用新Promise的resolve函数,
  25. //新Promise的fulfilAry现在为空,在新Promise的then操作后.新Promise的resolve执行
  26. result instanceof Promise ? result.then(resolve, reject) : resolve(result);
  27. })
  28. } catch(e) {
  29. reject(e);
  30. }
  31. });
  32. self._rejectQueue.push(() => {
  33. // 以下同理
  34. try {
  35. setTimeout(() => {
  36. const result = onRejected(self.error);
  37. // 不同点:此时是reject
  38. result instanceof Promise ? result.then(resolve, reject) : resolve(result);
  39. })
  40. } catch(e) {
  41. reject(e);
  42. }
  43. })
  44. } else if (self.status === FULFILLED) {
  45. try {
  46. setTimeout(() => {
  47. const result = onFulfilled(self.value);
  48. result instanceof Promise ? result.then(resolve, reject) : resolve(result);
  49. });
  50. } catch(e) {
  51. reject(e);
  52. }
  53. } else if (self.status === REJECTED){
  54. try {
  55. setTimeout(() => {
  56. const result = onRejected(self.error);
  57. result instanceof Promise ? result.then(resolve, reject) : resolve(result);
  58. })
  59. } catch(e) {
  60. reject(e);
  61. }
  62. }
  63. });
  64. }

测试:

  1. let p1 = new Promise((resolve, reject) => {
  2. setTimeout(() => {
  3. Math.random() < 0.5 ? resolve(100) : reject(-100);
  4. }, 1000);
  5. });
  6. let p2 = p1.then((result) => {
  7. //执行then返回的是一个新的Promise
  8. return result + 100;
  9. });
  10. let p3 = p2.then(
  11. (result) => {
  12. console.log(result);
  13. },
  14. (reason) => {
  15. console.log(reason);
  16. }
  17. );

catch()方法

Promise.prototype.catch就是Promise.prototype.then(null, onRejected)的别名,所以实现就很简单了:

  1. catch(onRejected) {
  2. return this.then(null, onRejected);
  3. }

resolve()

这里就不考虑参数是thenable对象了,那么参数有两种情况:

  1. Promise实例
  2. 不是Promise实例
  1. static resolve(value) {
  2. if (value instanceof Promise) {
  3. // 如果是Promise实例,直接返回
  4. return value;
  5. } else {
  6. // 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED
  7. return new Promise((resolve, reject) => resolve(value));
  8. }
  9. }

reject()

Promise.reject也会返回一个 Promise 实例,状态为REJECTED

Promise.resolve不同的是,Promise.reject方法的参数会原封不动地作为reject的参数

  1. static reject(error) {
  2. return new Promise((resolve, reject) => {
  3. reject(error);
  4. })
  5. }

all()

返回一个 promise 对象,只有当所有 promise 都成功时返回的 promise 状态才成功,需要注意的点是:

  1. 所有的 promise 状态变为FULFILLED,返回的 promise 状态才变为FULFILLED
  2. 一个 promise 状态变为REJECTED,返回的 promise 状态就变为REJECTED
  3. 数组成员不一定都是 promise,需要使用Promise.resolve()处理。
  1. static all(promiseArr) {
  2. let result = []
  3. // 记录已经成功执行的promise个数
  4. let count = 0;
  5. return new Promise((resolve, reject) => {
  6. promiseArr.forEach((p, i) => {
  7. // Promise.resolve()处理,确保每一个都是promise实例
  8. Promise.resolve(p).then(
  9. val => {
  10. result[i] = val;
  11. count++;
  12. // 如果全部执行完,返回promise的状态就可以改变了
  13. if (count === promiseArr.length) resolve(result);
  14. },
  15. err => reject(err), //只要有一个Promise被reject时,MyPromise的状态变为reject
  16. );
  17. }
  18. })
  19. }

race()

  1. static race(promiseArr) {
  2. return new Promise((resolve, reject) => {
  3. promiseArr.forEach(p => {
  4. Promise.resolve(p).then(
  5. val => resolve(val),
  6. err => reject(err),
  7. )
  8. })
  9. })
  10. }

完整代码

  1. // 模拟实现Promise
  2. // Promise利用三大手段解决回调地狱:
  3. // 1. 回调函数延迟绑定
  4. // 2. 返回值穿透
  5. // 3. 错误冒泡
  6. // 定义三种状态
  7. const PENDING = "PENDING"; // 进行中
  8. const FULFILLED = "FULFILLED"; // 已成功
  9. const REJECTED = "REJECTED"; // 已失败
  10. class Promise {
  11. constructor(exector) {
  12. if (typeof executor !== "function") {
  13. // 非标准 但与Chrome谷歌保持一致
  14. throw TypeError("Promise resolver " + executor + " is not a function");
  15. }
  16. // 初始化状态
  17. this.status = PENDING;
  18. // 将成功、失败结果放在this上,便于then、catch访问
  19. this.value = undefined;
  20. this.error = undefined;
  21. // 成功态回调函数队列
  22. this._resolveQueue = [];
  23. // 失败态回调函数队列
  24. this._rejectQueue = [];
  25. const resolve = (value) => {
  26. // 只有进行中状态才能更改状态
  27. if (this.status === PENDING) {
  28. this.status = FULFILLED;
  29. this.value = value;
  30. // 成功态函数依次执行
  31. this._resolveQueue.length > 0 &&this._resolveQueue.forEach((fn) => fn(this.value));
  32. }
  33. };
  34. const reject = (error) => {
  35. // 只有进行中状态才能更改状态
  36. if (this.status === PENDING) {
  37. this.status = REJECTED;
  38. this.error = error;
  39. // 失败态函数依次执行
  40. this._rejectQueue.length > 0 && this._rejectQueue.forEach((fn) => fn(this.error));
  41. }
  42. };
  43. try {
  44. // 立即执行executor
  45. // 把内部的resolve和reject传入executor,用户可调用resolve和reject
  46. exector(resolve, reject);
  47. } catch (e) {
  48. // executor执行出错,将错误内容reject抛出去
  49. reject(e);
  50. }
  51. }
  52. then(onFulfilled, onRejected) {
  53. onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
  54. onRejected = typeof onRejected === "function"
  55. ? onRejected
  56. : (error) => {
  57. throw new Error(error instanceof Error ? error.message : error);
  58. };
  59. // 保存this
  60. const self = this;
  61. return new Promise((resolve, reject) => {
  62. if (self.status === PENDING) {
  63. self._resolveQueue.push(() => {
  64. // try捕获错误
  65. try {
  66. // 模拟微任务
  67. setTimeout(() => {
  68. const result = onFulfilled(self.value);
  69. // 分两种情况:
  70. // 1. 回调函数返回值是Promise,执行then操作
  71. // 2. 如果不是Promise,调用新Promise的resolve函数
  72. result instanceof Promise
  73. ? result.then(resolve, reject)
  74. : resolve(result);
  75. });
  76. } catch (e) {
  77. reject(e);
  78. }
  79. });
  80. self._rejectQueue.push(() => {
  81. // 以下同理
  82. try {
  83. setTimeout(() => {
  84. const result = onRejected(self.error);
  85. // 不同点:此时是reject
  86. result instanceof Promise
  87. ? result.then(resolve, reject)
  88. : resolve(result);
  89. });
  90. } catch (e) {
  91. reject(e);
  92. }
  93. });
  94. } else if (self.status === FULFILLED) {
  95. try {
  96. setTimeout(() => {
  97. const result = onFulfilled(self.value);
  98. result instanceof Promise
  99. ? result.then(resolve, reject)
  100. : resolve(result);
  101. });
  102. } catch (e) {
  103. reject(e);
  104. }
  105. } else if (self.status === REJECTED) {
  106. try {
  107. setTimeout(() => {
  108. const result = onRejected(self.error);
  109. result instanceof Promise
  110. ? result.then(resolve, reject)
  111. : resolve(result);
  112. });
  113. } catch (e) {
  114. reject(e);
  115. }
  116. }
  117. });
  118. }
  119. catch(onRejected) {
  120. return this.then(null, onRejected);
  121. }
  122. //finally方法
  123. finally(callback) {
  124. return this.then(
  125. value => Promise.resolve(callback()).then(() => value), //执行回调,并returnvalue传递给后面的then
  126. reason => Promise.resolve(callback()).then(() => { throw reason }) //reject同理
  127. )
  128. }
  129. static resolve(value) {
  130. if (value instanceof Promise) {
  131. // 如果是Promise实例,直接返回
  132. return value;
  133. } else {
  134. // 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED
  135. return new Promise((resolve, reject) => resolve(value));
  136. }
  137. }
  138. static reject(error) {
  139. return new Promise((resolve, reject) => {
  140. reject(error);
  141. });
  142. }
  143. static all(promiseArr) {
  144. let result = []
  145. // 记录已经成功执行的promise个数
  146. let count = 0;
  147. return new Promise((resolve, reject) => {
  148. promiseArr.forEach((p, i) => {
  149. // Promise.resolve()处理,确保每一个都是promise实例
  150. Promise.resolve(p).then(
  151. val => {
  152. result[i] = val;
  153. count++;
  154. // 如果全部执行完,返回promise的状态就可以改变了
  155. if (count === promiseArr.length) resolve(result);
  156. },
  157. err => reject(err), //只要有一个Promise被reject时,MyPromise的状态变为reject
  158. );
  159. }
  160. })
  161. }
  162. static race(promiseArr) {
  163. return new Promise((resolve, reject) => {
  164. promiseArr.forEach((p) => {
  165. Promise.resolve(p).then(
  166. (val) => resolve(val),
  167. (err) => reject(err)
  168. );
  169. });
  170. });
  171. }
  172. }

9k 字 | Promise/async/Generator 实现原理解析
async/await 原理及执行顺序分析
ES6 系列之 Babel 将 Async 编译成了什么样子