前言

了解一个货色最好的方法之一就是入手本人写,So,他(Promise)来了。废话不多说,来看如何实现。

“五岳归来不看山,黄山归来不看岳。”心愿看完这篇,你就不必再去看其余 Promise 的实现原理了。

Promise 解析

先来看Promise用法:

new Promise((resolve, reject) => {    resolve('hello'); // or reject('hello')}) .then(res => {}) .catch(err => {})  -----------分割线// 合成一下,也就是上面这样let executor = (resolve, reject) => {    resolve('hello'); // or reject('hello')} new Promise(executor) .then(res => {}) .catch(err => {})

咱们来剖析一下他有哪些性能/个性

  • [x] 1、构造函数里传一个函数的两个参数(resolve, reject)
  • [x] 2、resolve 胜利时执行的回调
  • [x] 3、reject 失败时执行的回调
  • [x] 4、三种状态

    • pending [待定] 初始状态
    • fulfilled [实现] 操作胜利
    • rejected [被否决] 操作失败
  • [x] 5、Promise 对象办法 then
  • [x] 6、异步实现
  • [x] 7、onFulfilled 和 onRejected 的异步调用
  • [x] 8、值穿透
  • [x] 9、Promise 对象办法 catch
  • [x] 10、Promise 对象办法 all
  • [x] 11、Promise 对象办法 race
  • [x] 12、Promise 对象办法 resolve
  • [x] 13、Promise 对象办法 reject
  • [ ] 13、Promise 对象办法 allSettled(上个月 TC39 进去的新个性)

接下来,咱们要一一撕下他的假装,揭开他的真面目。

Promise 的根本构造实现

基于下面剖析后果,咱们先来实现后面三个性能:

  • [x] 1、构造函数里传一个函数的两个参数(resolve, reject)
  • [x] 2、resolve 胜利时执行的回调
  • [x] 3、reject 失败时执行的回调
class Promise {    constructor(executor) {        // 定义 resolve        let resolve = res => {}        // 定义 reject        let reject = err => {}        // 主动执行        executor(resolve, reject);    }}// 测试一下:new Promise((resolve, reject) => {    console.log('执行到啦~')})

能够将下面代码复制到控制台执行,查看成果:

Promise 三种状态实现

Ok,fine,接下来,咱们来实现她的三种状态。

  • [x] 4、三种状态

    • pending [待定] 初始状态
    • fulfilled [实现] 操作胜利
    • rejected [被否决] 操作失败

promise 状态有如下特点:
1.promise 对象初始化状态为 pending

2.当调用resolve(胜利),会由pending => fulfilled

3.当调用reject(失败),会由pending => rejected

Promsie 状态 只能由 pending => fulfilled/rejected, 一旦批改就不能再变
class Promise {    constructor(executor) {        this.status = "pending"; // 默认状态        this.value;  // resolve 胜利时的值        this.error;  // reject 失败时的值        let resolve = res => {            if(this.status === "pending") {                this.value = res;                this.status = "resolved";            }        }        let reject = err => {            if(this.status === "pending") {                this.error = err;                this.status = "rejected";            }        }        executor(resolve, reject);    }}

1)pending [待定] 初始状态

测试一下,如果不去resolve,也不去reject

// 测试一下:new Promise((resolve, reject) => {    })

那么Promise应该是初始状态。咱们将下面代码执行测试一下,失去后果如下:

此时状态是:{status: "pending"}

2)fulfilled [实现] 操作胜利

当咱们执行 resolve

// 测试一下:new Promise((resolve, reject) => {   resolve('胜利啦~'); })

将失去后果如下:

3)rejected [被否决] 操作失败

当执行 reject

// 测试一下:new Promise((resolve, reject) => {    resolve('失败啦~')})

Promise 对象办法 then 实现

  • [x] 5、Promise 对象办法 then

Promise 这个对象有 then 办法,还是先来剖析,then 有什么?

then 承受两个回调

promise.then(onFulfilled, onRejected); // 这里假如 promise 继承于 Promise 类

咱们持续在后面 Promise 类中书写 then 办法:

class Promise {        constructor(executor) {            this.status = "pending"; // 默认promise状态            this.value;  // resolve胜利时的值            this.error;  // reject失败时的值            let resolve = res => {                if(this.status === "pending") {                    this.value = res;                    this.status = "resolved";                }            }            let reject = err => {                if(this.status === "pending") {                    this.error = err;                    this.status = "rejected";                }            }            executor(resolve, reject)        }        // 申明 then        then(onFullfilled, onRejected) {            if(this.status === "resolved") {                onFullfilled(this.value)            }            if(this.status === "rejected") {                onRejected(this.error)            }        }    }

测试一下:

new Promise((resolve, reject) => {        resolve("胜利啦~"); // 或  reject("失败啦~")    })    .then(res => {        console.log(res);    }, err => {        console.log(err);    })

失去后果:

异步实现

  • [x] 6、异步实现

至此,根本实现简略的同步代码,然而当 resolve 在 setTimeout 内执行,then 时 state 还是 pending 期待状态。咱们就须要在 then 调用的时候,将胜利和失败存到各自的数组,一旦 reject 或者 resolve,就调用它们。

相似于散布订阅,先将 then 内的两个函数存储,因为 promise 能够有多个 then,所以存在同一个数组内。当胜利或失败的时候用 forEach 调用他们。

class Promise {        constructor(executor) {            this.status = "pending"; // 默认promise状态            this.value;  // resolve胜利时的值            this.error;  // reject失败时的值+           this.resolveQueue = []; // 胜利寄存的数组+           this.rejectQueue = []; // 失败寄存法数组            let resolve = value => {                if(this.status === "pending") {                    this.value = value;                    this.status = "resolved";                    // 一旦resolve执行,调用胜利数组的函数+                   this.resolveQueue.forEach(fn => fn());                }            }            let reject = value => {                if(this.status === "pending") {                    this.error = value;                    this.status = "rejected";                }                // 一旦reject执行,调用失败数组的函数+               this.rejectQueue.forEach(fn => fn());            }            executor(resolve, reject)        }                // 执行到then的时候        then(onFullfilled, onRejected) {            if(this.status === "resolved") {                this.resolveQueue.push(() => {                    onFullfilled(this.value);                })            }            if(this.status === "rejected") {                this.rejectQueue.push(() => {                    onRejected(this.error);                })            }            // 当状态state为pending时+           if(this.status === "pending") {                // onFulfilled传入到胜利数组+               this.resolveQueue.push(() => {+                   onFullfilled(this.value);+              })                // onRejected传入到失败数组+               this.rejectQueue.push(() => {+                   onRejected(this.error);+               })+           }        }    }

then 的链式调用

  • [x] 7、then 的链式调用

咱们经常用到new Promise().then().then()这样的写法,这就是链式调用,原来是用于解决天堂回调的。那么如何去实现呢?
为了达到这个成果,咱们能够再第一个 then 函数内再返回一个 Promise,让这个新的 Promise 返回的值传递到下一个 then 中。

一句话总结:

通过在 then 中 return 一个新的 Promise,从而实现 then 的链式调用!

代码如下:

class Promise {    constructor(executor) {        this.status = "pending"; // 默认promise状态        this.value;  // resolve 胜利时的值        this.error;  // reject 失败时的值        this.resolveQueue = []; // 胜利时回调队列        this.rejectQueue = []; // 失败时回调队列        let resolve = value => {            if(this.status === "pending") {                this.value = value;                this.status = "resolved";                this.resolveQueue.forEach(fn => fn())            }        }        let reject = value => {            if(this.status === "pending") {                this.error = value;                this.status = "rejected";                this.rejectQueue.forEach(fn => fn())            }        }        executor(resolve, reject)    }    then(onFullfilled, onRejected) {        let promise2;        if(this.status === "resolved") {            promise2 = new Promise((resolve, reject) => {                let x = onFullfilled(this.value);                resolvePromise(promise2, x, resolve, reject);            })        }        if(this.status === "rejected") {            promise2 = new Promise((resolve, reject) => {                let x = onRejected(this.value);                resolvePromise(promise2, x, resolve, reject);            })        }        if(this.status === "pending") {            promise2 = new Promise((resolve, reject) => {                this.resolveQueue.push(() => {                    let x = onFullfilled(this.value);                    resolvePromise(promise2, x, resolve, reject);                })                this.rejectQueue.push(() => {                    let x = onRejected(this.error);                    resolvePromise(promise2, x, resolve, reject);                })            })        }        return promise2;    }}-------------------分割线// 将下面代码整顿一下class Promise {    constructor(executor) {        this.status = "pending"; // 默认promise状态        this.value;  // resolve胜利时的值        this.error;  // reject失败时的值        this.resolveQueue = []; // 胜利时回调队列        this.rejectQueue = []; // 失败时回调队列        let resolve = value => {            if(this.status === "pending") {                this.value = value;                this.status = "resolved";                this.resolveQueue.forEach(fn => fn())            }        }        let reject = value => {            if(this.status === "pending") {                this.error = value;                this.status = "rejected";                this.rejectQueue.forEach(fn => fn())            }        }        executor(resolve, reject)    }    then(onFullfilled, onRejected) {        let promise2;        promise2 = new Promise((resolve, reject) => {            if(this.status === "resolved") {                let x = onFullfilled(this.value);                // resolvePromise函数,解决本人return的promise和默认的promise2的关系                resolvePromise(promise2, x, resolve, reject);            }            if(this.status === "rejected") {                let x = onRejected(this.value);                resolvePromise(promise2, x, resolve, reject);            }            if(this.status === "pending") {                this.resolveQueue.push(() => {                    let x = onFullfilled(this.value);                    resolvePromise(promise2, x, resolve, reject);                })                this.rejectQueue.push(() => {                    let x = onRejected(this.error);                    resolvePromise(promise2, x, resolve, reject);                })            }        });                // 返回 promise,达成链式成果        return promise2;    }}

最初,咱们来实现下面的 resolvePromise 函数,咱们暂且将第一个 then 返回的值成为 x,在这个函数中,咱们须要去判断 x 是不是 promise(这里是重点!):

  • 是:则取他的后果,作为新的 promise2 胜利的后果
  • 不是:间接作为新的 promise2 胜利的后果

resolvePromise 代码如下:

/** * 解决promise递归的函数 * * promise2 {Promise} 默认返回的promise * x {*} 咱们本人 return 的对象 * resolve * reject */ function resolvePromise(promise2, x, resolve, reject){    // 循环援用报错  if(x === promise2){    // reject 报错抛出    return reject(new TypeError('Chaining cycle detected for promise'));  }    // 锁,避免屡次调用  let called;    // x 不是 null 且 x 是对象或者函数  if (x != null && (typeof x === 'object' || typeof x === 'function')) {    try {      // A+ 规定,申明then = x的then办法      let then = x.then;            // 如果then是函数,就默认是promise了      if (typeof then === 'function') {         // then 执行 第一个参数是 this 前面是胜利的回调 和 失败的回调        then.call(x, y => {          // 胜利和失败只能调用一个          if (called) return;          called = true;                    // 外围点2:resolve 的后果仍旧是 promise 那就持续递归执行          // 外围点2:resolve 的后果仍旧是 promise 那就持续递归执行          // 外围点2:resolve 的后果仍旧是 promise 那就持续递归执行          resolvePromise(promise2, y, resolve, reject);        }, err => {          // 胜利和失败只能调用一个          if (called) return;          called = true;          reject(err);// 失败了就失败了        })      } else {        resolve(x); // 间接胜利即可      }    } catch (e) { // 走到 catch 也属于失败      if (called) return;      called = true;      // 取then出错了那就不要在继续执行了      reject(e);     }  } else {    resolve(x);  }}

then 链式调用测试

残缺测试代码如下,能够复制进浏览器控制台执行下:

function resolvePromise(promise2, x, resolve, reject){    // 循环援用报错    if(x === promise2){      // reject 报错抛出      return reject(new TypeError('Chaining cycle detected for promise'));    }    // 锁,避免屡次调用    let called;        // x不是null 且x是对象或者函数    if (x != null && (typeof x === 'object' || typeof x === 'function')) {      try {        // A+ 规定,申明then = x的then办法        let then = x.then;        // 如果then是函数,就默认是promise了        if (typeof then === 'function') {           // 就让then执行 第一个参数是this   前面是胜利的回调 和 失败的回调          then.call(x, y => {            // 胜利和失败只能调用一个            if (called) return;            called = true;            // resolve的后果仍旧是promise 那就持续递归执行            resolvePromise(promise2, y, resolve, reject);          }, err => {            // 胜利和失败只能调用一个            if (called) return;            called = true;            reject(err);// 失败了就失败了          })        } else {          resolve(x); // 间接胜利即可        }      } catch (e) {        // 也属于失败        if (called) return;        called = true;        // 取then出错了那就不要在继续执行了        reject(e);       }    } else {      resolve(x);    }  }class Promise {    constructor(executor) {        this.status = "pending"; // 默认promise状态        this.value;  // resolve胜利时的值        this.error;  // reject失败时的值        this.resolveQueue = []; // 胜利时回调队列        this.rejectQueue = []; // 失败时回调队列        let resolve = value => {            if(this.status === "pending") {                this.value = value;                this.status = "resolved";                this.resolveQueue.forEach(fn => fn())            }        }        let reject = value => {            if(this.status === "pending") {                this.error = value;                this.status = "rejected";                this.rejectQueue.forEach(fn => fn())            }        }        executor(resolve, reject)    }    then(onFullfilled, onRejected) {        let promise2;        promise2 = new Promise((resolve, reject) => {            if(this.status === "resolved") {                let x = onFullfilled(this.value);                // resolvePromise函数,解决本人return的promise和默认的promise2的关系                resolvePromise(promise2, x, resolve, reject);            }            if(this.status === "rejected") {                let x = onRejected(this.value);                resolvePromise(promise2, x, resolve, reject);            }            if(this.status === "pending") {                this.resolveQueue.push(() => {                    let x = onFullfilled(this.value);                    resolvePromise(promise2, x, resolve, reject);                })                this.rejectQueue.push(() => {                    let x = onRejected(this.error);                    resolvePromise(promise2, x, resolve, reject);                })            }        });                // 返回 promise,达成链式成果        return promise2;    }}// 测试以下代码new Promise((resolve, reject) => {    resolve();}).then((res)=>{    console.log('进入第一个then!')    return new Promise((resolve,reject)=>{        resolve('hello world');    })}).then((res)=>{    console.log('进入第二个then!', res);})


ok,咱们实现了 then 的链式调用,这也是实现 Promise 中的重难点!

onFulfilled 和 onRejected 的异步调用

  • [x] 8、onFulfilled 和 onRejected 的异步调用

外围思路:

用setTimeout解决异步问题

代码如下:

class Promise {    constructor(executor) {        this.status = "pending"; // 默认promise状态        this.value;  // resolve胜利时的值        this.error;  // reject失败时的值        this.resolveQueue = []; // 胜利时回调队列        this.rejectQueue = []; // 失败时回调队列        let resolve = value => {            if(this.status === "pending") {                this.value = value;                this.status = "resolved";                this.resolveQueue.forEach(fn => fn())            }        }        let reject = value => {            if(this.status === "pending") {                this.error = value;                this.status = "rejected";                this.rejectQueue.forEach(fn => fn())            }        }        executor(resolve, reject)    }    then(onFullfilled, onRejected) {        let promise2;        promise2 = new Promise((resolve, reject) => {            if(this.status === "resolved") {                // 异步+               setTimeout(() => {                    let x = onFullfilled(this.value);                    // resolvePromise函数,解决本人return的promise和默认的promise2的关系                    resolvePromise(promise2, x, resolve, reject);+               }, 0)            }            if(this.status === "rejected") {                // 异步+               setTimeout(() => {                    let x = onRejected(this.value);                    resolvePromise(promise2, x, resolve, reject);+               }, 0)            }            if(this.status === "pending") {                this.resolveQueue.push(() => {                    // 异步+                   setTimeout(() => {                        let x = onFullfilled(this.value);                        resolvePromise(promise2, x, resolve, reject);+                   }, 0)                })                this.rejectQueue.push(() => {                    // 异步+                   setTimeout(() => {                        let x = onRejected(this.error);                        resolvePromise(promise2, x, resolve, reject);+                   }, 0)                })            }        });                // 返回 promise,达成链式成果        return promise2;    }}

值穿透调用

  • [x] 9、值穿透
new Promise((resolve, reject)=>{    resolve('YoYo');}).then().then().then().then().then().then().then((res)=>{     console.log(res);})

当执行下面多个 then,咱们冀望最初那个 then 打印出 'YoYo'。

实现很简略:onFulfilled 如果不是函数,就疏忽 onFulfilled,间接返回 value!

相应的,咱们也要解决下没有 onRejected 的状况:onRejected 如果不是函数,就疏忽 onRejected,间接扔出谬误!

代码如下,在之前的 Promise 类的 then 退出:

then(onFulfilled, onRejected) {    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err;}        <!--... 省略-->}

Promise 对象办法 catch

  • [x] 10、Promise 对象办法 catch

外围思路:

catch 是失败的回调,相当于执行 this.then(null,fn)
class Promise {        constructor(executor) {           <!--... 省略-->        }        then(onFullfilled, onRejected) {            <!--... 省略-->        }        +       catch(onRejected) {+           return this.then(null, onRejected)+       }    }

此外,咱们还须要对其余几个函数外应用 try/catch 去做异样捕捉,这里不开展,了解即可(本文最初源码中会展现)。

Promise 对象办法 all

  • [x] 10、Promise 对象办法 all

这是一道经典面试题!

Promise.all() 接管一个数组作为参数,该办法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“实现(resolved)”或参数中不蕴含 promise 时回调实现(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败的起因是第一个失败 promise 的后果。

用法如下:

var p1 = Promise.resolve(3);var p2 = 1337;var p3 = new Promise((resolve, reject) => {  setTimeout(resolve, 100, 'foo');}); Promise.all([p1, p2, p3]).then(values => {   console.log(values); // [3, 1337, "foo"] });

接下来看看如何实现:
上面手写的,没有测试,回头补充!上班先~

Promise.all = function(promises) {    let count = 0;    let res = [];    return new Promise((resolve, reject) => {        for(let i = 0; i<promises.length; i++) {            promises[i].then(res => {                res.push(res);                count++;                if(count === promises.length) resolve(res);            })        }    })    .catch(err => {        reject(err);    })}

Promise 对象办法 race

  • [x] 11、Promise 对象办法 race

Promise.race() 它同样接管一个promise对象组成的数组作为参数,并返回一个新的promise对象。一旦迭代器中的某个promise解决或回绝,返回的 promise就会解决或回绝。

Promise.race = function(promises) {    return new Promise((resolve, reject) => {        for(let i = 0; i<promises.length; i++) {            promises[i].then(resolve, reject);        }    })}

Promise 对象办法 resolve

  • [x] 12、Promise 对象办法 resolve
Promise.resolve = function(value) {    return new Promise((resolve, reject) => {        resolve(value);    })}

Promise 对象办法 reject

  • [x] 13、Promise 对象办法 reject
Promise.reject = function(value) {    return new Promise((resolve, reject) => {        reject(value);    })}

Promise 对象办法 allSettled

当课后作业吧,同学们本人写,能够放回复中哈~

Reference:
Promises/A+