手写实现Promise/A+规范详解

892 阅读11分钟

手写实现Promise详解

如果还不是很了解Promise的小伙伴,可以先查看阮一峰大佬写的Promise对象的基本用法

中间都是实现流程想直接看源码的大佬请直接点击源码查看或从参考文档中查看

Promise的基本语法

const p=new Promeise((resolve,reject)=>{
	resolve() //reject()
})
p.then((res)=>{},(err)=>{}).catch((err)=>{})

按照基本语法实现

一、创建一个MyPromsie类

const p=new Promeise()
//实现
class MyPromsie{}

二、在constructor中添加基础方法

const p=new Promeise((resolve,reject)=>{
	resolve() //reject()
})
p.then((res)=>{},(err)=>{}).catch((err)=>{})
///实现
class MyPromise {
    constructor (handle) {
        // 为了接受 new Promeise((resolve,reject)=>{}) 传入的(resolve,reject)=>{}函数
        try {
            //定义一个handle接收(resolve,reject)=>{}函数 函数又会传入两个参数 resolve,reject 因此需要定义这两个参数并传入
            handle(this._resolve, this._reject)
        } catch (err) {
        	//报错时处理
            this._reject(err)
        }
    }
    //定义函数传入的两个参数
    //成功时调用
    _resolve= val => {}
    //失败时调用
    _resolve= err> {}
    //then方法 接受两个参数 一个完成时回调函数 一个拒绝时回调函数
    //p.then((res)=>{},(err)=>{})
    then(onFulfilled, onRejected){}
}

三、完善方法先实现同步调用 异步调用在第四节

const p=new Promeise((resolve,reject)=>{
	resolve() //reject()
})
///实现
// 定义Promise的三种状态常量 用于函数执行时的状态判断和保持promise结果确定后就不能在改变
const PENDING = 'pending'; //等待
const FULFILLED = 'fulfilled'; //已完成
const REJECTED = 'rejected'; //已拒绝
class MyPromise {
    constructor (handle) {
         // 为了接受 new Promeise((resolve,reject)=>{}) 传入的(resolve,reject)=>{}函数
        try {
            //定义一个handle接收(resolve,reject)=>{}函数 函数又会传入两个参数 resolve,reject 因此需要定义这两个参数
            handle(this._resolve, this._reject)
        } catch (err) {
            this._reject(err)
        }
    }
    //存储完成或拒绝的值
    _value = undefined
    //存储状态 pending  fulfilled  rejected
    _status = PENDING
    //定义函数传入的两个参数
    //完成时调用
    _resolve = val => {
        //如果状态改变 就直接返回
        if (this._status !== PENDING) return;
        //状态设置为已完成
        this._status = FULFILLED
        //赋值已完成时传入的参数
        this._value = val;
    }
    //拒绝时调用
    _reject = err => {
        //如果状态改变 就直接返回
        if(this._status !== PENDING) return;
        //状态设置为拒绝
        this._status = REJECTED
        //赋值拒绝时传入的参数
    	this._value = err;
    }
     //then方法 接受两个参数 一个完成时回调函数 一个拒绝时回调函数
    //p.then((res)=>{},(err)=>{})
    then(onFulfilled, onRejected) {
        let {_status, _value} = this
        switch (_status) {
            // 当状态为pending时,将then方法回调函数加入执行队列等待执行
            case PENDING:
                //先留到后面
                break;
            // 当状态已经改变时,立即执行对应的回调函数
            case FULFILLED:
                onFulfilled(_value);
                break;
            case REJECTED:
                onRejected(_value);
                break;
        }
    }
}
module.exports = MyPromise
  • 代码运行结果

四、支持异步调用 在代码中添加队列

将then传入的函数如果状态是成功直接执行,如果还在pending放入队列中,然后resole/reject函数回调后直接调用队列中的方法。(没有使用一个变量来保存回调函数,而是使用队列是因为有可能出现多个p.then方法,调用多次的p.then()就会传入多个待执行的方法,只用变了保存的话就只会保存最后一个)

!

  • 完整代码
const PENDING = 'pending'; //等待
const FULFILLED = 'fulfilled'; //已完成
const REJECTED = 'rejected'; //已拒绝
class MyPromise {
    constructor (handle) {
        // new Promeise((resolve,reject)=>{})
        try {
            //定义一个handle处理行数(resolve,reject)=>{}
            handle(this._resolve, this._reject)
        } catch (err) {
            this._reject(err)
        }
    }
    //存储完成或拒绝的值
    _value = undefined
    //存储状态 pending  fulfilled  rejected
    _status = PENDING
    // 失败队列,在 then 时注入,resolve 时触发
    _rejectedQueues = [];
    // 成功队列,在 then 时注入,resolve 时触发
    _fulfilledQueues = [];
    //定义函数传入的两个参数
    //完成时调用
    _resolve = val => {
        //如果状态改变 就直接返回
        if (this._status !== PENDING) return;
        // 依次执行成功队列中的函数,并清空队列
        const runFulfilled = value => {
            let cb;
            while ((cb = this._fulfilledQueues.shift())) {
                cb(value);
            }
        };
        //状态设置为已完成
        this._status = FULFILLED
        //赋值已完成时传入的参数
        this._value = val;
        runFulfilled(val)
    }
    //拒绝时调用
    _reject = err => {
        //如果状态改变 就直接返回
        if (this._status !== PENDING) return;
        //状态设置为拒绝
        this._status = REJECTED
        //赋值拒绝时传入的参数
        this._value = err;
        let cb;
        while ((cb = this._rejectedQueues.shift())) {
            cb(err);
        }
    }
    //then方法 接受两个参数 一个完成时回调函数 一个决绝时回调函数
    then(onFulfilled, onRejected) {
        let {_status, _value} = this
        switch (_status) {
            // 当状态为pending时,将then方法回调函数加入执行队列等待执行
            case PENDING:
                this._fulfilledQueues.push(onFulfilled);
                this._rejectedQueues.push(onRejected)
                break;
            // 当状态已经改变时,立即执行对应的回调函数
            case FULFILLED:
                onFulfilled(_value);
                break;
            case REJECTED:
                onRejected(_value);
                break;
        }
    }
}

module.exports = MyPromise
  • 运行结果

五、如果promise里面返回的时另外一个Pomise对象需要等待另外一个promise返回状态后才执行当前的promise且状态取决于参数Promsie对象的状态

列:p等待了两秒以后才返回 嘎嘎嘎 的值

  • 代码片段
  • 完整代码
const PENDING = 'pending'; //等待
const FULFILLED = 'fulfilled'; //已完成
const REJECTED = 'rejected'; //已拒绝
class MyPromise {
    constructor (handle) {
        // new Promeise((resolve,reject)=>{})
        try {
            //定义一个handle处理行数(resolve,reject)=>{}
            handle(this._resolve, this._reject)
        } catch (err) {
            this._reject(err)
        }
    }
    //存储完成或拒绝的值
    _value = undefined
    //存储状态 pending  fulfilled  rejected
    _status = PENDING
    // 失败队列,在 then 时注入,resolve 时触发
    _rejectedQueues = [];
    // 成功队列,在 then 时注入,resolve 时触发
    _fulfilledQueues = [];
    //定义函数传入的两个参数
    //完成时调用
  _resolve = val => {
    //如果状态改变 就直接返回
    if (this._status !== PENDING) return;
    // 依次执行成功队列中的函数,并清空队列
    const runFulfilled = value => {
      let cb;
      while ((cb = this._fulfilledQueues.shift())) {
        cb(value);
      }
    };
    // 依次执行失败队列中的函数,并清空队列
    const runRejected = error => {
      let cb;
      while ((cb = this._rejectedQueues.shift())) {
        cb(error);
      }
    };
    const run = () => {
      this._status = FULFILLED;

      /*
      * 如果resolve的参数为Promise对象,
      * 则必须等待该Promise对象状态改变后当前Promsie的状态才会改变
      * 且状态取决于参数Promsie对象的状态
  */
      if (val instanceof MyPromise) {
        val.then(value => {
          this._value = value;
          runFulfilled(value)
        }, err => {
          this._value = err;
          runRejected(err);
        })
      } else {
        this._value = val;
        runFulfilled(val)
      }
    }
    //使用微任务执行和ES6 Promise保持一致 如果使用seTimeOut会到宏任务队列中无法和promiise保持你一致
    queueMicrotask(run)
  }
    //拒绝时调用
    _reject = err => {
      //如果状态改变 就直接返回
    if (this._status !== PENDING) return;
    const run = () => {
      //状态设置为拒绝
      this._status = REJECTED
      //赋值拒绝时传入的参数
      this._value = err;
      let cb;
      while ((cb = this._rejectedQueues.shift())) {
        cb(err);
      }
    }
         //使用微任务执行和ES6 Promise保持一致 如果使用seTimeOut会到宏任务队列中无法和promiise保持你一致
    	queueMicrotask(run)
    }
    //then方法 接受两个参数 一个完成时回调函数 一个决绝时回调函数
    then(onFulfilled, onRejected) {
        let {_status, _value} = this
        switch (_status) {
            // 当状态为pending时,将then方法回调函数加入执行队列等待执行
            case PENDING:
                this._fulfilledQueues.push(onFulfilled);
                this._rejectedQueues.push(onRejected)
                break;
            // 当状态已经改变时,立即执行对应的回调函数
            case FULFILLED:
                onFulfilled(_value);
                break;
            case REJECTED:
                onRejected(_value);
                break;
        }
    }
}

module.exports = MyPromise

*拓展

上面代码中使用了queueMicrotask方法是微任务调用行数执行这样保持了Es6 promise实现一致,形成异步调用不会造成阻塞,如果使用setTimeout就变成了宏任务 也不会造成阻塞 不过执行顺序将发生变化(有兴趣的可以和我说后面说下事件循环机制)

下面显示下不同方法所显示的不同结果

  • 运行结果 同列子

六、then后面还是会返回一个promise对象就可以实现链式调用后面再接then方法

const p=new Promeise((resolve,reject)=>{
	resolve() //reject()
})
p.then((res)=>{},(err)=>{}).then((res)=>{})
  • 完整代码 (代码修改then部分)
const isFunction = variable => typeof variable === 'function';

const PENDING = 'pending'; //等待
const FULFILLED = 'fulfilled'; //已完成
const REJECTED = 'rejected'; //已拒绝
class MyPromise {
    constructor (handle) {
        // new Promeise((resolve,reject)=>{})
        try {
            //定义一个handle处理行数(resolve,reject)=>{}
            handle(this._resolve, this._reject)
        } catch (err) {
            this._reject(err)
        }
    }
    //存储完成或拒绝的值
    _value = undefined
    //存储状态 pending  fulfilled  rejected
    _status = PENDING
    // 失败队列,在 then 时注入,resolve 时触发
    _rejectedQueues = [];
    // 成功队列,在 then 时注入,resolve 时触发
    _fulfilledQueues = [];
    //定义函数传入的两个参数
   //完成时调用
  _resolve = val => {
    //如果状态改变 就直接返回
    if (this._status !== PENDING) return;
    // 依次执行成功队列中的函数,并清空队列
    const runFulfilled = value => {
      let cb;
      while ((cb = this._fulfilledQueues.shift())) {
        cb(value);
      }
    };
    // 依次执行失败队列中的函数,并清空队列
    const runRejected = error => {
      let cb;
      while ((cb = this._rejectedQueues.shift())) {
        cb(error);
      }
    };
    const run = () => {
      this._status = FULFILLED;

      /*
      * 如果resolve的参数为Promise对象,
      * 则必须等待该Promise对象状态改变后当前Promsie的状态才会改变
      * 且状态取决于参数Promsie对象的状态
  */
      if (val instanceof MyPromise) {
        val.then(value => {
          this._value = value;
          runFulfilled(value)
        }, err => {
          this._value = err;
          runRejected(err);
        })
      } else {
        this._value = val;
        runFulfilled(val)
      }
    }
    //使用微任务执行和ES6 Promise保持一致 如果使用seTimeOut会到宏任务队列中无法和promiise保持你一致
    queueMicrotask(run)
  }
  //拒绝时调用
  _reject = err => {
    //如果状态改变 就直接返回
    if (this._status !== PENDING) return;
    const run = () => {
      //状态设置为拒绝
      this._status = REJECTED
      //赋值拒绝时传入的参数
      this._value = err;
      let cb;
      while ((cb = this._rejectedQueues.shift())) {
        cb(err);
      }
    }
     //使用微任务执行和ES6 Promise保持一致 如果使用seTimeOut会到宏任务队列中无法和promiise保持你一致
    queueMicrotask(run)
  }
    //then方法 接受两个参数 一个完成时回调函数 一个决绝时回调函数
    then(onFulfilled, onRejected) {
        let {_status, _value} = this
        // then返回的是一个Promise对象
        return new MyPromise((resolve, reject) => {
            // 封装一个完成时执行的函数
            const fulfilled = value => {
                try {
                    if (!isFunction(onFulfilled)) {
                        resolve(value);
                    } else {
                        const res = onFulfilled(value);
                        if (res instanceof MyPromise) {
                            // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                            res.then(resolve, reject);
                        } else {
                            //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                            resolve(res);
                        }
                    }
                } catch (err) {
                    // 如果函数执行出错,新的Promise对象的状态为失败
                    reject(err);
                }
            };
            // 封装一个拒绝时执行的函数
            const rejected = err => {
                try {
                    if (!isFunction(onRejected)) {
                        resolve(error);
                    } else {
                        const res = onRejected(err);
                        if (res instanceof MyPromise) {
                            // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                            res.then(resolve, reject);
                        } else {
                            //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                            resolve(res);
                        }
                    }
                } catch (err) {
                    reject(err);
                }
            }
            // console.log('_status', _status)
            switch (_status) {
                // 当状态为pending时,将then方法回调函数加入执行队列等待执行
                case PENDING:
                    this._fulfilledQueues.push(fulfilled);
                    this._rejectedQueues.push(rejected)
                    break;
                // 当状态已经改变时,立即执行对应的回调函数
                case FULFILLED:
                    fulfilled(_value);
                    break;
                case REJECTED:
                    rejected(_value);
                    break;
            }
        })
    }
}

module.exports = MyPromise
  • 运行结果

以上基本是promise的主要代码下面时参考文档中的所有Promise方法的代码

源码

/**
 * 参考 文档  
 * https://juejin.cn/post/6844903989083897870#heading-13
 * https://es6.ruanyifeng.com/#docs/promise
 *  */
const isFunction = variable => typeof variable === 'function';

const PENDING = 'pending'; //等待
const FULFILLED = 'fulfilled'; //已完成
const REJECTED = 'rejected'; //已拒绝
class MyPromise {
    constructor (handle) {
        // new Promeise((resolve,reject)=>{})
        try {
            //定义一个handle处理行数(resolve,reject)=>{}
            handle(this._resolve, this._reject)
        } catch (err) {
            this._reject(err)
        }
    }
    //存储完成或拒绝的值
    _value = undefined
    //存储状态 pending  fulfilled  rejected
    _status = PENDING
    // 失败队列,在 then 时注入,resolve 时触发
    _rejectedQueues = [];
    // 成功队列,在 then 时注入,resolve 时触发
    _fulfilledQueues = [];
    //定义函数传入的两个参数
   //完成时调用
  _resolve = val => {
    //如果状态改变 就直接返回
    if (this._status !== PENDING) return;
    // 依次执行成功队列中的函数,并清空队列
    const runFulfilled = value => {
      let cb;
      while ((cb = this._fulfilledQueues.shift())) {
        cb(value);
      }
    };
    // 依次执行失败队列中的函数,并清空队列
    const runRejected = error => {
      let cb;
      while ((cb = this._rejectedQueues.shift())) {
        cb(error);
      }
    };
    const run = () => {
      this._status = FULFILLED;

      /*
      * 如果resolve的参数为Promise对象,
      * 则必须等待该Promise对象状态改变后当前Promsie的状态才会改变
      * 且状态取决于参数Promsie对象的状态
  */
      if (val instanceof MyPromise) {
        val.then(value => {
          this._value = value;
          runFulfilled(value)
        }, err => {
          this._value = err;
          runRejected(err);
        })
      } else {
        this._value = val;
        runFulfilled(val)
      }
    }
    //使用微任务执行和ES6 Promise保持一致 如果使用seTimeOut会到宏任务队列中无法和promiise保持你一致
    queueMicrotask(run)
  }
  //拒绝时调用
  _reject = err => {
    //如果状态改变 就直接返回
    if (this._status !== PENDING) return;
    const run = () => {
      //状态设置为拒绝
      this._status = REJECTED
      //赋值拒绝时传入的参数
      this._value = err;
      let cb;
      while ((cb = this._rejectedQueues.shift())) {
        cb(err);
      }
    }
     //使用微任务执行和ES6 Promise保持一致 如果使用seTimeOut会到宏任务队列中无法和promiise保持你一致
    queueMicrotask(run)
  }
    //then方法 接受两个参数 一个完成时回调函数 一个决绝时回调函数
    then(onFulfilled, onRejected) {
        let {_status, _value} = this
        // then返回的是一个Promise对象
        return new MyPromise((resolve, reject) => {
            // 封装一个完成时执行的函数
            const fulfilled = value => {
                try {
                    if (!isFunction(onFulfilled)) {
                        resolve(value);
                    } else {
                        const res = onFulfilled(value);
                        if (res instanceof MyPromise) {
                            // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                            res.then(resolve, reject);
                        } else {
                            //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                            resolve(res);
                        }
                    }
                } catch (err) {
                    // 如果函数执行出错,新的Promise对象的状态为失败
                    reject(err);
                }
            };
            // 封装一个拒绝时执行的函数
            const rejected = err => {
                try {
                    if (!isFunction(onRejected)) {
                        resolve(error);
                    } else {
                        const res = onRejected(err);
                        if (res instanceof MyPromise) {
                            // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                            res.then(resolve, reject);
                        } else {
                            //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                            resolve(res);
                        }
                    }
                } catch (err) {
                    reject(err);
                }
            }
            // console.log('_status', _status)
            switch (_status) {
                // 当状态为pending时,将then方法回调函数加入执行队列等待执行
                case PENDING:
                    this._fulfilledQueues.push(fulfilled);
                    this._rejectedQueues.push(rejected)
                    break;
                // 当状态已经改变时,立即执行对应的回调函数
                case FULFILLED:
                    fulfilled(_value);
                    break;
                case REJECTED:
                    rejected(_value);
                    break;
            }
        })
    }
        // catch 方法
        catch(onRejected) {
            return this.then(undefined, onRejected);
        }
        // finally 方法
        finally(cb) {
            return this.then(
                value => MyPromise.resolve(cb()).then(() => value),
                reason =>
                    MyPromise.resolve(cb()).then(() => {
                        throw reason;
                    })
            );
        }
        // 静态 resolve 方法
        static resolve(value) {
            // 如果参数是MyPromise实例,直接返回这个实例
            if (value instanceof MyPromise) return value;
            return new MyPromise(resolve => resolve(value));
        }
        // 静态 reject 方法
        static reject(value) {
            return new MyPromise((resolve, reject) => reject(value));
        }
        // 静态 all 方法
        static all(list) {
            return new MyPromise((resolve, reject) => {
                // 返回值的集合
                let values = [];
                let count = 0;
                for (let [i, p] of list.entries()) {
                    // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
                    this.resolve(p).then(
                        res => {
                            values[i] = res;
                            count++;
                            // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
                            if (count === list.length) resolve(values);
                        },
                        err => {
                            // 有一个被rejected时返回的MyPromise状态就变成rejected
                            reject(err);
                        }
                    );
                }
            });
        }
        // 添加静态race方法
        static race(list) {
            return new MyPromise((resolve, reject) => {
                for (let p of list) {
                    // 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
                    this.resolve(p).then(
                        res => {
                            resolve(res);
                        },
                        err => {
                            reject(err);
                        }
                    );
                }
            });
        }
}

module.exports = MyPromise

参考文档