你也可以手写自己的Promise(一)

2,018 阅读4分钟

我写这篇文章不打算介绍Promise产生的原因以及它解决的问题,我只是想写一篇关于实现自己Promise的文章。如果代码以及逻辑有什么不对的地方,请大家指出来。就这些,开始正题。

前提:我们要知道Promise是基于Promises/A+规范的。其中好多变量和方法名都是从这里来的。 我们先从Promise的使用开始,写几个测试例子。

let promise = new Promise((resolve, reject) =>{
    // console.log("1");
    // resolve("成功");
    // reject("失败");
    // console.log("2");// 第一步


    // reject("失败");
    // resolve("成功");// 第二步

    // setTimeout(() => {
    //     resolve("success");
    // }, 2000);

    throw new Error("手动抛出错误");// 第四步
});
promise.then((value) => {
    console.log("then第一个方法:"+value);
}, (err) => {
    console.log("then第二个方法:"+err);
})
promise.then((value) => {
    console.log("then第一个方法:"+value);
}, (err) => {
    console.log("then第二个方法:"+err);
})
console.log("3");

第一步输出

  • 1
  • 2
  • 3
  • then第一个方法:成功
  • then第一个方法:成功

第二步输出

  • 3
  • then第二个方法:失败
  • then第二个方法:失败

第三步输出

  • 3 两秒之后
  • then第一个方法:success
  • then第一个方法:success

第四步输出

  • 3
  • then第二个方法:Error: 手动抛出错误
  • then第二个方法:Error: 手动抛出错误

最后输出“成功”说明 then是异步执行的

根据以上几个例子我们可以推出以下几点内容:

  • Promise是一个构造函数(使用了 new)
  • Promise接收一个参数,并且这个参数是一个函数(为了方便描述,我们称之为 executor)
  • executor在 new Promise时执行
  • new Promise中可以支持异步行为(第三步)
  • executor有两个参数(resolve,reject)
  • resolve和reject不会我们传进去的,说明是属于Promise内容提供的
  • resolve和reject都是函数,并且都接收一个参数。看规范:resolve接收参数称之为value,reject接收参数称之为reason
  • 每个Promise实例上都有then方法
  • then方法是异步的
  • then方法中有两个参数onFulfilled和onRejected 分别是成功的回调(执行resolve)和失败的回调(执行reject)。看这里
  • 一个Promise中resolve和reject只会执行一个,规范中有提到 Promise States,大家可以看下
  • 同一个promise的实例可以then多次,成功时回调用所有的成功方法,失败时回调用所有的失败方法
  • 如果发现错误就会走入失败态

这么一大坨东西,看着有点乱。我们就根据我们得出的结论开始写属于自己的Promise。写的过程中思路慢慢就清晰了。

let myPromise = function (executor) {
    let self = this;//缓存一下this

    self.status = 'pending';// 状态管理 状态的变化只能由pending变为resolved或者rejected。一件事情不能既成功又失败。所以resolved和rejected不能相互转化。
    self.value = undefined;// 成功后的值 传给resolve
    self.reason = undefined;//失败原因 传给reject

    self.onResolvedCallbacks = [];// 存放then中成功的回调
    self.onRejectedCallbacks = []; // 存放then中失败的回调 
    // 这里说明一下,第三步使用定时器。执行完 new Promise 之后,会执行then方法,此时会把then中的方法缓存起来,并不执行:此时状态还是pending。等到定时器2秒之后,执行
    // resolve|reject 时,而是依次执行存放在数组中的方法。 参考发布订阅模式

    function resolve(value) {
        // pending => resolved
        if (self.status === 'pending') {
            self.value = value;
            self.status = 'resolved';
            // 依次执行缓存的成功的回调
            self.onResolvedCallbacks.forEach(fn => fn(self.value));
        }
    }

    function reject(reason) {
        // pending => rejected
        if (self.status === 'pending') {
            self.value = value;
            self.status = 'rejected';
            // 依次执行缓存的失败的回调
            self.onRejectedCallbacks.forEach(fn => fn(self.reason));
        }
    }

    try {
        //new Promise 时 executor执行
        executor(resolve, reject);
    } catch (error) {
        reject(error);// 当executor中执行有异常时,直接执行reject
    }
}

// 每个Promise实例上都有then方法
Promise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;

    // 执行了 resolve
    if (self.status === 'resolved') {
        // 执行成功的回调
        onFulfilled(self.value);
    }

    // 执行了 reject
    if (self.status === 'rejected') {
        // 执行失败的回调
        onRejected(self.reason);
    }

    // new Promise中可以支持异步行为 当既不执行resolve又不执行reject时 状态是默认的等待态pending
    if (self.status === 'pending') {
        // 缓存成功的回调
        self.onResolvedCallbacks.push(onFulfilled);
        // 缓存失败的回调
        self.onRejectedCallbacks.push(onRejected);
    }
};

说明一下:这是最简版,因为Promise的强大之处是链式调用。我们这个只是雏形,由于时间关系。我们先到这里。下一次我们基于这个雏形实现符合Promises/A+规范的完整版。

第一次发表文章,希望各位大虾多多支持。