1. 明确需求

在 Promise 中,then 方法和 catch 方法都是可以通过链式调用这种形式无限调用下去的。

所以考虑以下几个改造点:

  • then方法中应该直接把 this 给 return 出去(链式调用常规操作);
  • 链式调用允许我们多次调用 then,多个 then 中传入的 onResolved(也叫onFulFilled) 和 onRejected 任务,我们需要把它们维护在一个队列里;
  • 要想办法确保 then 方法执行的时机,务必在 onResolved 队列 和 onRejected 队列批量执行前。不然队列任务批量执行的时候,任务本身都还没收集完。一个比较容易想到的办法就是把批量执行这个动作包装成异步任务,这样就能确保它一定可以在同步代码之后执行了。

2. 编码

2.1 构造函数

  1. function CutePromise(executor) {
  2. // value 记录异步任务成功的执行结果
  3. this.value = null;
  4. // reason 记录异步任务失败的原因
  5. this.reason = null;
  6. // status 记录当前状态,初始化是 pending
  7. this.status = 'pending';
  8. // 缓存两个队列,维护 resolved 和 rejected 各自对应的处理函数
  9. this.onResolvedQueue = [];
  10. this.onRejectedQueue = [];
  11. // 把 this 存下来,后面会用到
  12. var self = this;
  13. // 定义 resolve 函数
  14. function resolve(value) {
  15. // 如果不是 pending 状态,直接返回
  16. if (self.status !== 'pending') {
  17. return;
  18. }
  19. // 异步任务成功,把结果赋值给 value
  20. self.value = value;
  21. // 当前状态切换为 resolved
  22. self.status = 'resolved';
  23. // 用 setTimeout 延迟队列任务的执行
  24. setTimeout(function(){
  25. // 批量执行 resolved 队列里的任务
  26. self.onResolvedQueue.forEach(resolved => resolved(self.value));
  27. });
  28. }
  29. // 定义 reject 函数
  30. function reject(reason) {
  31. // 如果不是 pending 状态,直接返回
  32. if (self.status !== 'pending') {
  33. return;
  34. }
  35. // 异步任务失败,把结果赋值给 value
  36. self.reason = reason;
  37. // 当前状态切换为 rejected
  38. self.status = 'rejected';
  39. // 用 setTimeout 延迟队列任务的执行
  40. setTimeout(function(){
  41. // 批量执行 rejected 队列里的任务
  42. self.onRejectedQueue.forEach(rejected => rejected(self.reason));
  43. });
  44. }
  45. // 把 resolve 和 reject 能力赋予执行器
  46. try{
  47. execute(resolve, reject)
  48. }catch(e){
  49. reject(e)
  50. }
  51. }

2.2 then 方法

  1. // then 方法接收两个函数作为入参(可选)
  2. CutePromise.prototype.then = function(onResolved, onRejected) {
  3. // 注意,onResolved 和 onRejected必须是函数;如果不是,我们此处用一个透传来兜底
  4. if (typeof onResolved !== 'function') {
  5. onResolved = function(x) {return x};
  6. }
  7. if (typeof onRejected !== 'function') {
  8. onRejected = function(e) {throw e};
  9. }
  10. // 依然是保存 this
  11. var self = this;
  12. // 判断是否是 resolved 状态
  13. if (self.status === 'resolved') {
  14. // 如果是 执行对应的处理方法
  15. onResolved(self.value);
  16. } else if (self.status === 'rejected') {
  17. // 若是 rejected 状态,则执行 rejected 对应方法
  18. onRejected(self.reason);
  19. } else if (self.status === 'pending') {
  20. // 若是 pending 状态,则只对任务做入队处理
  21. self.onResolvedQueue.push(onResolved);
  22. self.onRejectedQueue.push(onRejected);
  23. }
  24. return this
  25. };

3. 测试

  1. const cutePromise = new CutePromise(function (resolve, reject) {
  2. resolve('成了!');
  3. });
  4. cutePromise.then((value) => {
  5. console.log(value)
  6. console.log('我是第 1 个任务')
  7. }).then(value => {
  8. console.log('我是第 2 个任务')
  9. });
  10. // 依次输出“成了!” “我是第 1 个任务” “我是第 2 个任务”

image.png