1. 前言

Promises/A+标准:
https://www.ituring.com.cn/article/66566(中文);https://promisesaplus.com/(英文);
ES6 中的promise采纳 Promise/A+ 标准,链接如上。

源码起源:https://repl.it/@morrain2016/Promise
本文本质上是对这份源码的一次解析。
这份源码是用JS代码对promise的模仿实现,与原生的promise势必存在些许的差别,然而,了解了这份源码,必定会帮忙你更深刻地了解promise底层原理。

2. 源码解析

本章节次要分成两个大节对源码进行解析:

  1. 尽可能地将源码缩减至最小,但保障了其外围性能的实现。通过这一阶段,你就深刻了解了promise的外围原理;
  2. 残缺源码的其它局部详解。

2.1 外围代码

本大节分为两个局部,第一个局部为没有正文的外围代码,第二局部为具体正文的外围代码,目标很简略:读者先本人思考,若在某处卡住了可返回第二局部找到答案。
指标是:实现一个myPromise类,能胜利执行上面的代码:

new myPromise(res => {    setTimeout(() => {        console.log("test1");    // 两秒后输入 test1        res("data1");    }, 2000);}).then((data) => {    console.log(data);    // 两秒后紧接着test1输入 data1    return new myPromise(res => {        setTimeout(() => {            console.log("test2");    // 三秒后输入 test2            res("data2");        }, 1000);    })}).then(data => {    console.log(data);    // 三秒后紧接着test2输入 data2})

2.1.1 challenge

该局部代码只保留了promise的两种状态:pendingfulfilled,对外只暴露出 then 办法。rejected 状态和其它办法将在第二阶段解析。

无正文版:

class myPromise {    callbacks = [];    state = 'pending';    value = null;    constructor(fn) {        fn(this._resolve.bind(this));    }    then(onFulfilled) {        return new myPromise((resolve) => {            this._handle({                onFulfilled: onFulfilled || null,                resolve: resolve,            });        });    }    _handle(callback) {        if (this.state === 'pending') {            this.callbacks.push(callback);            return;        }        if (!callback.onFulfilled) {            callback.resolve(this.value);            return;        }        var ret = callback.onFulfilled(this.value);        callback.resolve(ret);    }    _resolve(value) {        if (this.state !== 'pending') return        if (value && (typeof value === 'object' && value !== null || typeof value === 'function')) {            var then = value.then;            if (typeof then === 'function') {                then.call(value, this._resolve.bind(this));                return;            }        }        this.state = 'fulfilled';        this.value = value;        this.callbacks.forEach(callback => this._handle(callback));    }}

2.1.2 help

具体正文版:

class myPromise {    callbacks = [];// resolve执行后,会顺次执行该数组中的回调函数    state = 'pending';//记录promise的以后状态: "pending"/"fulfilled"/"rejected"    value = null;//记录resolve(data)中传递的数据data    constructor(fn) {        // 立刻执行传入promise中的函数fn        // 当fn函数中执行resolve(),其实是在调用外部办法:this._resolve.bind(this)        fn(this._resolve.bind(this));    }    // promise实例的then办法, 将传进来的函数 onFulfilled 放入callback中    then(onFulfilled) {        // then办法返回一个新的promise实例, 以实现链式调用: promise.then().then()....        return new myPromise((resolve) => {            // 这个新promise实例的状态由resolve管制, 所以将resolve一并打包, 前面会用到            this._handle({                onFulfilled: onFulfilled || null,                resolve: resolve,            });        });    }    _handle(callback) {        // 顺着then办法的逻辑, 将callback对象放入存储数组中        if (this.state === 'pending') {            this.callbacks.push(callback);            return;        }        // 如果then中没有传递参数, 即模式为 promise.then().then(cb),         // 那么第一个then发明的promise实例, 应该立刻执行resolve, 进入fulfilled状态, 并执行cb();        if (!callback.onFulfilled) {            callback.resolve(this.value);//将value传递上来            return;        }        /**         * promise.then(()=>{         *      return 666;         * }).then(data =>{         *      console.log(data); // 打印出666         * })         * 这里相当于在执行第一个then中的回调函数, 并将返回的 "666" 传递给下一个then;         *          * 思考: 如果返回的不是 "666", 而是一个promise呢? 能够去看_resolve办法中标记为 **(1)** 的代码逻辑         */        var ret = callback.onFulfilled(this.value);        callback.resolve(ret);    }    _resolve(value) {        // 这行代码的成果就是: new myPromise(res=>{res(); res(); res();}), 第一个res()前面的所有res()都会疏忽        if (this.state !== 'pending') return        // **(1)** 这一块的逻辑基于value是一个promise实例.      这一块倡议最初钻研        if (value && (typeof value === 'object' && value !== null || typeof value === 'function')) {            var then = value.then;// promise领有then办法, 据此来判断该对象是不是一个promise            if (typeof then === 'function') {                // value是一个promise实例, 此时以后promise的状态该当依赖于 value这个promise实例的状态                then.call(value, this._resolve.bind(this));                 return;                // 看个例子                /**                 * promise.then(()={                 *      return new myPromise(res => {                 *          res(data);                 *      })                 * })                 * 这里回调函数中返回的是一个promise实例, 相当于下面的value                 * then.call(value, this._resolve.bind(this)), 即value.then(this._resolve.bind(this)),                 * 当value这个promise履行执行fulfilled后, 就会执行 this._resolve.bind(this), 也就是外层promise的 resolve(data)                 */            }        }        // 上面的逻辑就比较简单了        this.state = 'fulfilled';//扭转状态        this.value = value;//保留后果        this.callbacks.forEach(callback => this._handle(callback));//顺次调用callbacks数组中的所有函数    }}

2.2 其它局部详解

如果读者已通过第一阶段,倡议拜访该地址https://www.ituring.com.cn/article/66566,间接浏览残缺源码,若途中遇到问题,可回到本大节来找到答案。

本大节次要从以下几个局部进行解析:

  1. rejected 状态的补充;
  2. 类的静态方法:resolve、reject、all、race ;
  3. 实例的catchfinally办法。

2.2.1 rejected 状态的补充

class myPromise {  callbacks = [];  state = 'pending';  value = null;  constructor(fn) {    // 这里的参数多了一个_reject函数    fn(this._resolve.bind(this), this._reject.bind(this));  }  then(onFulfilled, onRejected) {    return new myPromise((resolve, reject) => {      // 这里减少了一部分代码      this._handle({        onFulfilled: onFulfilled || null,        onRejected: onRejected || null,        resolve: resolve,        reject: reject      });    });  }  _handle(callback) {    if (this.state === 'pending') {      this.callbacks.push(callback);      return;    }    // 这里和上个版本的逻辑相似, 只不过减少了 rejected 状态    let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;    if (!cb) {      cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;      cb(this.value);      return;    }    /**     * cb函数在执行过程中可能会遇到报错, 如     * promise.then(()=>{     *    throw Error("error!")     * })     * 这时该当执行reject, 并将error信息传递进来     */    let ret;    try {      ret = cb(this.value);      cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;    } catch (error) {      ret = error;      cb = callback.reject    } finally {      cb(ret);    }  }  _resolve(value) {    if (this.state !== 'pending') return    if (value && (typeof value === 'object' || typeof value === 'function')) {      var then = value.then;      if (typeof then === 'function') {        then.call(value, this._resolve.bind(this), this._reject.bind(this));        return;      }    }    this.state = 'fulfilled';//扭转状态    this.value = value;//保留后果    this.callbacks.forEach(callback => this._handle(callback));  }  // 减少了_reject外部办法  _reject(error) {    if (this.state !== 'pending') return    /**     * 这里没有对error是不是promise的判断, 因而当onRejected函数返回一个promise实例时,会间接将这个实例传递进来,如     *  new myPromise((res, rej) => {          // 将一个promise实例传入rej()函数中          rej(new myPromise(() => {console.log("test")}));          }).then(() => { }, data => {          console.log(data);  // 这里将打印出下面的promise实例        })     */    this.state = 'rejected';    this.value = error;    this.callbacks.forEach(callback => this._handle(callback));  }}

2.2.2 类的静态方法(resolve、reject、all、race )

  • Promise.resolve()、Promise.reject():
    // Promise.resolve()返回一个promise实例, 上面的代码都围绕着这一点, 只是对传进来的不同参数响应不同的行为    static resolve(value) {      if (value && value instanceof Promise) {        // value是promise实例, 间接返回该实例        return value;      } else if (value && typeof value === 'object' && typeof value.then === 'function') {        // value不是promise实例, 然而具备then办法; 即它是一个 thenable 对象        let then = value.then;        return new Promise(resolve => {          then(resolve);        });      } else if (value) {        // value 不是promise实例也不是thenable对象        return new Promise(resolve => resolve(value));      } else {        // value不存在        return new Promise(resolve => resolve());      }    }    static reject(value) {      if (value && typeof value === 'object' && typeof value.then === 'function') {        let then = value.then;        return new Promise((resolve, reject) => {          then(reject);        });              } else {        return new Promise((resolve, reject) => reject(value));      }    }

Promise.reject 与 Promise.resolve 相似,区别在于 Promise.reject 始终返回一个状态的 rejected 的 Promise 实例,而 Promise.resolve 的参数如果是一个 Promise实例的话,返回的是参数对应的 Promise 实例,所以状态不肯定。

  • Promise.all()、Promise.race()
static all(promises) {      // 返回一个promise实例      return new Promise((resolve, reject) => {        let fulfilledCount = 0  // 计数        const itemNum = promises.length // promises中的promise个数        const rets = Array.from({ length: itemNum })  // 用来保留每个promise的返回后果        promises.forEach((promise, index) => {          Promise.resolve(promise).then(result => {            fulfilledCount++;            rets[index] = result;            if (fulfilledCount === itemNum) {              resolve(rets);            }          }, reason => reject(reason));        })        })    }    static race(promises) {      return new Promise(function (resolve, reject) {        // 顺次执行promises中的各个promise,只有有一个promise状态确定了, 就执行resolve或者reject        for (let i = 0; i < promises.length; i++) {          Promise.resolve(promises[i]).then(function (value) {            return resolve(value)          }, function (reason) {            return reject(reason)          })        }      })    }

2.2.3 实例办法(catch、finally)

// promise.catch(cb)相当于 promise.then(null, cb)catch(onError) {      return this.then(null, onError);    }finally(onDone) {      if (typeof onDone !== 'function') return this.then();      let Promise = this.constructor;      return this.then(        value => Promise.resolve(onDone()).then(() => value),        reason => Promise.resolve(onDone()).then(() => { throw reason })      );    // 如果这里写成: return this.then(onDone, onDone), 会导致这样的问题:     // 1.onDone会接管上一个promise传过来的参数; 2.如果onDone返回一个promise, 它将影响最终的状态;    }

3. 源码的缺点

这份源码存在一些问题,比方在执行上面这段代码时:原生promise的输入后果是: 2, 1,因为then办法中的代码是异步执行的;而这份源码实现的promise的输入后果是:1,2,这次要是因为以后源码的then办法是同步执行。

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

4. 参考资料

图解 Promise 实现原理:https://zhuanlan.zhihu.com/p/58428287

【翻译】Promises/A+标准:https://www.ituring.com.cn/article/66566

源码:https://repl.it/@morrain2016/Promise