最最最通俗易懂的promise手写系列(二)- 链式调用

前言

上一次我们写完了Promise的基础功能,明白了promise为何能在任务成功的时候,调成功的回调函数,为何在任务失败的时候调失败的回调函数.今天就把最麻烦的promise链式调用给搞定吧.

附上上篇文章的链接:最最最通俗易懂的promise手写系列(一)

再附上上次的代码吧,以免翻来翻去麻烦.

  function Promise(executor) {
    let self = this;
    self.value = undefined;
    self.reason = undefined;
    self.status = 'pending';

    self.onFulFilledCallbacks = [];
    self.onRejectedCallbacks = [];

    function resolve(value) {
        if (self.status === 'pending') {
            self.value = value;
            self.status = 'resolved'

            self.onFulFilledCallbacks.forEach(onFulFilled => {
                onFulFilled(self.value)
            });
        }
    }
    function reject(reason) {
        if (self.status === 'pending') {
            self.reason = reason;
            self.status = 'rejected';
            self.onRejectedCallbacks.forEach(onRejected => {
                onRejected(self.reason)
            });
        }
    }

    try {
        executor(resolve, reject);
    } catch (error) {
        reject(error)
    }
}

Promise.prototype.then = function (onFulFilled, onRejected) {
    if (this.status === 'pending') {
        this.onFulFilledCallbacks.push(() => {
            onFulFilled(this.value)
        }); 
        this.onRejectedCallbacks.push(() => {
            onRejected(this.reason)
        })
    } else if (this.status === 'resolved') {
        onFulFilled(this.value);
    } else if (this.status === 'rejected') {
        onRejected(this.reason);
    }
}
复制代码

我们要完成的目标:

let p = new Promise(function (resove, reject) {
  setTimeout(() => {
    console.log('任务执行完了');
    resove()
  },1500)
});

p.then(function (value) {
      console.log('第一个成功回调')
    },function () {})
  .then(function () {
      console.log('第二个成功回调')
    }, function () {});
    
输出如下:
任务执行完了
第一个成功回调
第二个成功回调
复制代码
  • 我们都熟悉Jquery,它的链式调用是用rerun this来做的,可是这里却不行,原因文章末尾再解释。 我们采取返回一个新的promise对象来实现链式调用.
  • 意思也就是p.then()返回一个新promise对象.
//给这个函数加个返回值,返回值就是一个新new的promise对象
Promise.prototype.then = function (onFulFilled, onRejected) {
    let p2 = new Promise((resolve, reject) => {});
    
    if (this.status === 'pending') {
        this.onFulFilledCallbacks.push(() => {
            onFulFilled(this.value)
        }); 
        this.onRejectedCallbacks.push(() => {
            onRejected(this.reason)
        })
    } else if (this.status === 'resolved') {
        onFulFilled(this.value);
    } else if (this.status === 'rejected') {
        onRejected(this.reason);
    }
    return p2;
}
复制代码
  • 加了两行代码,我们测试一下输出结果:
    任务执行完了
    第一个成功回调
  • 根据上一次我们所讲的可以总结出如下结论:真正触发onFulFilledCallbacks里所存储的回调函数只有两个地方:
  1. resolve被调用的时候.(resolve是用户调用的,因为用户当然知道哪种逻辑算任务成功,哪种逻辑算任务失败,例如我们上一章的例子,随机数大于5我就认为是成功的.)
  2. then被调用的时候.(then也是用户调用的,promise里的任务执行完了后要做啥)

这里我们理一下流程:在我们new Promise的时候,executor就同步的执行了,根据executor里有无异步操作分一下两种情况:

1.有异步操作,如我们的例子里,有setTimout,延时1.5s后打印。

  • executor函数执行完,p也就完成了new的过程。(此时还不会打印输出任务执行完了,因为setTimout是异步的)
  • 拿到p对象后,代码接着执行p.then,此时很明显状态是pending,我们会把用户在then里传的回调函数存起来(onFulFilledCallbacks),在1.5s后,setTimeout里调用resovle的时候,会遍历onFulFilledCallbacks,执行之前then里传的函数
  • 这也就是我们之前为什么要给promise加三个状态的原因(等待pending 成功resolve 失败rejected),害怕里面有异步任务。 不管如何异步,用户总是知道代码走到哪儿算是成功了(在成功的地方调resolve),代码走到哪儿算是出异常了(在失败的地方调reject)这是规矩,使用Promise必须遵守的规矩。

2.没有异步操作,如我们的例子里,有setTimout,延时1.5s后打印。

  • 这个就简单了,跟着代码顺序看就行了,执行executor直接就会调resolve,then的会后,任务已经完成,当即执行用户传的回调函数就行了

回到我们要解决的问题刚只是输出了第一个成功回调,因为【p2】的status是pending呀(我们在内部自己new的Promise,内部没有任何要执行的东西,没有调resolve,那我们就调一下呗).

//给这个函数加个返回值,返回值就是一个新new的promise对象
Promise.prototype.then = function (onFulFilled, onRejected) {
    let p2 = new Promise((resolve, reject) => { resove() });
    
    if (this.status === 'pending') {
        this.onFulFilledCallbacks.push(() => {
            onFulFilled(this.value)
        }); 
        this.onRejectedCallbacks.push(() => {
            onRejected(this.reason)
        })
    } else if (this.status === 'resolved') {
        onFulFilled(this.value);
    } else if (this.status === 'rejected') {
        onRejected(this.reason);
    }
    return p2;
}
复制代码
  • 我们测试一下输出结果:
    第二个成功回调
    任务执行完了
    第一个成功回调
  • 这一会倒是执行了,可是顺序乱了,原因:p.then拿到p2,p2立刻就resolve了,当代码走到p2.then自然就是直接走到this.status === 'resolved',执行传入的函数了.等1.5s后第一个promsie任务完成,打印任务执行完了调用第一个的resolve,第一个成功回调
  • 我们想要的是自己控制顺序,看直白一点就是我们得控制在第一个Promise也就是P任务执行完了,才能调P2.resove,那就直接在第一个Promise回调函数执行完了我们再调P2.resove吧
//给这个函数加个返回值,返回值就是一个新new的promise对象
Promise.prototype.then = function (onFulFilled, onRejected) {
  //p2的resolve在里面,外面拿不到,只有这样很贱的给在外面记下来了
  let p2Resolve ;
  let p2Reject;
  let p2 = new Promise((resolve, reject) => {
    p2Resolve = resolve;
    p2Reject = reject;
  });

  if (this.status === 'pending') {
    this.onFulFilledCallbacks.push(() => {
      onFulFilled(this.value)
      p2Resolve()
    });
    this.onRejectedCallbacks.push(() => {
      onRejected(this.reason)
      p2Reject()
    })
  } else if (this.status === 'resolved') {
    onFulFilled(this.value);
    p2Resolve()
  } else if (this.status === 'rejected') {
    onRejected(this.reason);
    p2Reject()
  }

  return p2;
}
复制代码

输出如下:
任务执行完了
第一个成功回调
第二个成功回调

结语

我们剩下onFulFilled返回值的功能没做,下次子再来.
感谢各位观众.

猜你喜欢

转载自juejin.im/post/5b5c24ebe51d45195f0b4cfc