手写实现promise

101 阅读11分钟

PromiseA+的规范

讲解promiseA+规范前,先了解一些术语,以便在后续提到专有名词时有明确且统一的规范。

术语

  1. promise是一个有then方法的对象或者是函数,行为遵循本规范
  2. thenable是一个有then方法的对象或者函数
  3. value是promise状态成功时的值,也就是resolve的参数,包括各种数据类型,也包括undefined/thenable或者promise
  4. reason是promise状态失败时的值,也就是reject的参数,表示拒绝的原因
  5. exception是一个使用throw抛出的异常值

规范

接下来分几部分来讲解promiseA+规范

Promise State

promise应该有三种状态,要注意他们之间的流转关系

  1. pending
  • 初始的状态,可改变
  • 一个promise在resolve或者reject前都处于这个状态
  • 可以通过resolve =>fulfilled状态
  • 可以通过reject => rejected状态
  1. fulfilled
  • 最终态,不可变
  • 一个promise被resolve后变成这个状态
  • 必须拥有一个value值
  1. rejected
  • 最终态,不可变
  • 一个promise被reject后会变成这个状态
  • 必须拥有一个reason Tips:总结一下,就是promise的状态流转是这样的
  • pending => resolve(value) => fulfilled
  • pending => reject(reason) => rejected
then

promise应该提供一个then方法,用来访问最终的结果,无论是value还是reason

promise.then(onFulfilled,onRejected)
  1. 参数要求
  • onFulfilled 必须是函数类型, 如果不是函数, 应该被忽略
  • onRejected 必须是函数类型, 如果不是函数, 应该被忽略
  1. onFulfilled 特性
  • 在promise变成 fulfilled 时,应该调⽤ onFulfilled, 参数是value
  • 在promise变成 fulfilled 之前, 不应该被调⽤
  • 只能被调⽤⼀次(所以在实现的时候需要⼀个变量来限制执⾏次数)
  1. onRejected 特性
  • 在promise变成 rejected 时,应该调⽤ onRejected, 参数是reason
  • 在promise变成 rejected 之前, 不应该被调⽤
  • 只能被调⽤⼀次(所以在实现的时候需要⼀个变量来限制执⾏次数)
  1. onFulfilled 和 onRejected 应该是微任务
  • 这⾥⽤queueMicrotask来实现微任务的调⽤
  1. then⽅法可以被调⽤多次
  • promise状态变成 fulfilled 后,所有的 onFulfilled 回调都需要按照then的顺序执⾏, 也就 是按照注册顺序执⾏(所以在实现的时候需要⼀个数组来存放多个onFulfilled的回调)
  • promise状态变成 rejected 后,所有的 onRejected 回调都需要按照then的顺序执⾏, 也 就是按照注册顺序执⾏(所以在实现的时候需要⼀个数组来存放多个onRejected的回调)
  1. 返回值
  • then应该返回一个promise
promise2 = promise1.then(onFulfilled,onRejected);
  • onFulfilled 或 onRejected 执⾏的结果为x, 调⽤ resolvePromise( 这⾥⼤家可能难以理 解, 可以先保留疑问, 下⾯详细讲⼀下resolvePromise是什么东⻄ )
  • 如果 onFulfilled 或者 onRejected 执⾏时抛出异常e, promise2需要被reject
  • 如果 onFulfilled 不是⼀个函数, promise2 以promise1的value 触发fulfilled
  • 如果 onRejected 不是⼀个函数, promise2 以promise1的reason 触发rejected
  1. resolvePromise
resolvePromise(promise2, x, resolve, reject)
  • 如果 promise2 和 x 相等,那么 reject TypeError
  • 如果 x 是⼀个 promsie 如果x是pending态,那么promise必须要在pending,直到 x 变成 fulfilled or rejected. 如果 x 被 fulfilled, fulfill promise with the same value. 如果 x 被 rejected, reject promise with the same reason
  • 如果 x 是⼀个 object 或者 是⼀个 function let then = x.then. 如果 x.then 这步出错,那么 reject promise with e as the reason. 如果 then 是⼀个函数,then.call(x, resolvePromiseFn, rejectPromise) resolvePromiseFn 的 ⼊参是 y, 执⾏ resolvePromise(promise2, y, resolve, reject); rejectPromise 的 ⼊参是 r, reject promise with r. 如果 resolvePromise 和 rejectPromise 都调⽤了,那么第⼀个调⽤优先,后⾯的调⽤忽 略。 如果调⽤then抛出异常e 如果 resolvePromise 或 rejectPromise 已经被调⽤,那么忽略 则,reject promise with e as the reason 如果 then 不是⼀个function. fulfill promise with x

一步步实现一个Promise

  1. 平常⽤promise的时候, 是通过new关键字来new Promise(), 所以咱们应该⽤构造函数或者class 来实现. 都2021年了, 咱们就⽤class来实现⼀下吧
class Mpromise{
    constructor(){
    
    }
}
  1. 定义三种状态类型
const PENDING = 'pending'; 
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
  1. 设置初始状态
class Mpromise{
    constructor(){
     // 初始状态为pending
     this.status = PENDING;
     this.value = null;
     this.reason = null;
    }
}
  1. resolve和reject方法
  • 根据刚才的规范, 这两个⽅法是要更改status的, 从pending改到fulfilled/rejected
  • 注意两个函数的⼊参分别是value 和 reason
class MPromise { 
    constructor() { 
        // 初始状态为pending 
        this.status = PENDING; 
        this.value = null;
        this.reason = null; 
    };
    resolve(value) {
        if (this.status === PENDING) { 
            this.value = value;
            this.status = FULFILLED; 
        } 
    };
    reject(reason) { 
        if (this.status === PENDING) {
            this.reason = reason; 
            this.status = REJECTED;
        }
    }
}
  1. 是不是发现咱们的promise少了⼊参, 咱们来加⼀下
  • ⼊参是⼀个函数, 函数接收resolve和reject两个参数
  • 注意在初始化promise的时候, 就要执⾏这个函数, 并且有任何报错都要通过reject抛出去
class MPromise { 
    constructor(fn) { 
        // 初始状态为pending 
        this.status = PENDING; 
        this.value = null;
        this.reason = null; 
        try {
            fn(this.resolve.bind(this),this.reject.bind(this))
        } catch (e) {
            this.reject(e)
        }
    };
    resolve(value) {
        if (this.status === PENDING) { 
            this.value = value;
            this.status = FULFILLED; 
        } 
    };
    reject(reason) { 
        if (this.status === PENDING) {
            this.reason = reason; 
            this.status = REJECTED;
        }
    }
}
  1. 接下来实现关键的then方法
  • then接收两个参数, onFulfilled 和 onRejected
then(onFulfilled,onRejected) {}
  • 检查并处理参数, 之前提到的如果不是function, 就忽略. 这个忽略指的是原样返回value或者 reason
isFunction(param){
    return typeof param==='function';
};

then(onFulfilled,onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : 
    (value) => {
        return value 
     };
    const realOnRejected = this.isFunction(onRejected) ? onRejected : 
    (reason) => { 
        throw reason
    };
};
  • 要知道.then的返回值整体是⼀个promise, 所以咱们先⽤promise来包裹⼀下, 其他逻辑待会 再实现
then(onFulfilled,onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : 
    (value) => {
        return value 
     };
    const realOnRejected = this.isFunction(onRejected) ? onRejected : 
    (reason) => { 
        throw reason
    };
    const promise2 = new MPromise((resolve, reject)=> {});
    return promise2;
};
  • 根据当前promise的状态,调用不同的函数
then(onFulfilled,onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : 
    (value) => {
        return value 
     };
    const realOnRejected = this.isFunction(onRejected) ? onRejected : 
    (reason) => { 
        throw reason
    };
    const promise2 = new MPromise((resolve, reject)=> {
        switch (this.status) {
            case FULFILLED:{
                realOnFulfilled();
                break;
            }
            case REJECTED:{
                realOnRejected();
                break;
            }
        
        }
    });
    return promise2;
};
  • 提问:如果这样写, 是在then函数被调⽤的瞬间就会执⾏. 那这时候如果 status还没变成fulfilled或者rejected怎么办, 很有可能还是pending的. 所以我们需要⼀个状 态的监听机制, 当状态变成fulfilled或者rejected后, 再去执⾏callback
  • 那么我们⾸先要拿到所有的callback, 然后才能在某个时机去执⾏他. 新建两个数组, 来分别 存储成功和失败的回调, 调⽤then的时候, 如果还是pending就存⼊数组
FULFILLED_CALLBACK_LIST = []; 
REJECTED_CALLBACK_LIST = [];

then(onFulfilled,onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : 
    (value) => {
        return value 
     };
    const realOnRejected = this.isFunction(onRejected) ? onRejected : 
    (reason) => { 
        throw reason
    };
    const promise2 = new MPromise((resolve, reject)=> {
        switch (this.status) {
            case FULFILLED:{
                realOnFulfilled();
                break;
            }
            case REJECTED:{
                realOnRejected();
                break;
            }
            case PENDING:{
                this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled);
                this.REJECTED_CALLBACK_LIST.push(realOnRejected);
            }
        
        }
    });
    return promise2;
};
  • 在status发⽣变化的时候, 就执⾏所有的回调. 这⾥咱们⽤⼀下es6的getter和setter. 这样更 符合语义, 当status改变时, 去做什么事情. (当然也可以顺序执⾏, 在给status赋值后, 下⾯再 加⼀⾏forEach)
_status = PENDING;
get status () {
    return this._status;
}
set status(newStatus){
    this._status = newStatus;
    switch (newStatus){
        case FULFILLED:{
            this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                callback(this.value);
            });
            break;
        }
        case REJECTED:{
            this.REJECTED_CALLBACK_LIST.forEach(callback => {
                callback(this.reson);
            });
            break;
        }
    }
}

  1. then的返回值,⾯只是简单说了下, then的返回值是⼀个Promise, 那么接下来具体讲⼀下返回promise的 value和reason是什么
  • 如果 onFulfilled 或者 onRejected 抛出⼀个异常 e ,则 promise2 必须拒绝执⾏,并返回拒 因 e。(这样的话, 我们就需要⼿动catch代码,遇到报错就reject)
then(onFulfilled,onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : 
    (value) => {
        return value 
     };
    const realOnRejected = this.isFunction(onRejected) ? onRejected : 
    (reason) => { 
        throw reason
    };
    const promise2 = new MPromise((resolve, reject)=> {
        const fulfilledMicrotask = () => {
            try{
                realOnFulfilled(this.value)
            } catch (e){
                reject(e)
            }
        }
        const rejectedMicrotask = () => {
            try{
                realOnRejected(this.reason)
            } catch (e){
                reject(e)
            }
        }
    
        switch (this.status) {
            case FULFILLED:{
                fulfilledMicrotask();
                break;
            }
            case REJECTED:{
                rejectedMicrotask();
                break;
            }
            case PENDING:{
                this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled);
                this.REJECTED_CALLBACK_LIST.push(realOnRejected);
            }
        
        }
    });
    return promise2;
};
  • 如果 onFulfilled 不是函数且 promise1 成功执⾏, promise2 必须成功执⾏并返回相同 的值
  • 如果 onRejected 不是函数且 promise1 拒绝执⾏, promise2 必须拒绝执⾏并返回相同 的据因。 需要注意的是,如果promise1的onRejected执⾏成功了,promise2应该被resolve 这⾥咱们其实已经在参数检查的时候做过了, 也就是这段代码
const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : 
    (value) => {
        return value 
     };
    const realOnRejected = this.isFunction(onRejected) ? onRejected : 
    (reason) => { 
        throw reason
    };
  • 如果 onFulfilled 或者 onRejected 返回⼀个值 x ,则运⾏resolvePromise⽅法
then(onFulfilled,onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : 
    (value) => {
        return value 
     };
    const realOnRejected = this.isFunction(onRejected) ? onRejected : 
    (reason) => { 
        throw reason
    };
    const promise2 = new MPromise((resolve, reject)=> {
        const fulfilledMicrotask = () => {
            try{
                const x = realOnFulfilled(this.value);
                this.resolvePromise(promise2, x, resolve, reject)
            } catch (e){
                reject(e)
            }
        }
        const rejectedMicrotask = () => {
            try{
                const x = realOnRejected(this.reason);
                this.resolvePromise(promise2, x, resolve, reject)
            } catch (e){
                reject(e)
            }
        }
    
        switch (this.status) {
            case FULFILLED:{
                fulfilledMicrotask();
                break;
            }
            case REJECTED:{
                rejectedMicrotask();
                break;
            }
            case PENDING:{
                this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled);
                this.REJECTED_CALLBACK_LIST.push(realOnRejected);
            }
        
        }
    });
    return promise2;
};
  1. 实现resolvePromise
resolvePromise(promise2, x, resolve, reject){
    // 如果 newPromise 和 x 指向同⼀对象,以 TypeError 为据因拒绝执⾏ newPromise
    // 这里为了防止死循环
    if(promise2 === x){
        return reject( new TypeError('The promise and the return value are the same'))
    }
    
    if(x instanceof MPromise){
        // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
        // 也就是继续执⾏x,如果执⾏的时候拿到⼀个y,还要继续解析y
        queueMicrotask(() => {
            x.then((y) => {
                this.resolvePromise(promise2, y, resolve, reject)
            }, reject)
        })
    } else if (typeof x === 'object' || this.isFunction(x)){
         // 如果 x 为对象或者函数
         if( x===null ){
         // null也会被判断为对象
             return resolve(x);
         }
        
        let then =null;
        
        try {
            // 把 x.then 赋值给 then
            then = x.then;
        } catch (error){
            // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
            return reject(error)
        }
    
        // 如果 then 是函数
        if(this.isFunction(then)){
            let called = false;
            // 将 x 作为函数的作⽤域 this 调⽤
            // 传递两个回调函数作为参数,第⼀个参数叫做 resolvePromise ,第⼆个参数叫做 rejectPromise
            try {
                then.call(
                    x,
                    // 如果 resolvePromise 以值 y 为参数被调⽤,则运⾏ resolvePromise
                    (y) => {
                        // 需要有⼀个变量called来保证只调⽤⼀次
                        if(called) return;
                        called = true;
                        this.resolvePromise(promise2, y, resolve, reject);
                    },
                    // 如果 rejectPromise 以据因 r 为参数被调⽤,则以据因 r 拒绝 promise
                    (r) => {
                        if(called) return;
                        called = true;
                        reject(r);
                    }
                )
            } catch (error){
                // 如果调用 then 方法就抛出异常 e;
                if(called) return;
                // 否则以e为据因拒绝promise
                reject(error)
            }
            
        } else {
            // 如果then不是函数,以x 为参数执行promise
            resolve(x)
        }
    
    } else {
        // 如果 x 不为对象或者函数,以 x 为参数执⾏ promise
        resolve(x)
    }

}
  1. onFulfilled 和 onRejected 是微任务,需要用window自带的queueMicrotask函数来处理
const fulfilledMicrotask = () => {
    queueMicrotask(() => {
        try{
            const x = realOnFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject)
        } catch (e){
            reject(e)
        }
    })         
}
const rejectedMicrotask = () => {
    queueMicrotask(() => {
        try{
            const x = realOnRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject)
        } catch (e){
            reject(e)
        }
    })         
}
        
  1. 补充catch方法
catch (onRejected) {
    return this.then(null, onRejected)
}
  1. promise.resolve
  • 将现有对象转为Promise对象,如果 Promise.resolve ⽅法的参数,不是具有 then ⽅法的对 象(⼜称 thenable 对象),则返回⼀个新的 Promise 对象,且它的状态为fulfilled。 注意这是⼀个静态⽅法, 因为咱们是通过Promise.resolve调⽤的, ⽽不是通过实例去调⽤的
static resolve (value){
    if(value instanceof MPromise){
        return value;
    }
    
    return new MPromise((resolve) => {
        resolve(value)
    })
}
  1. promise.reject
  • 返回⼀个新的Promise实例,该实例的状态为rejected。Promise.reject⽅法的参数reason, 会被传递给实例的回调函数
static reject (reason) {
    return new MPromise((resolve,reject)=>{
        reject(reason)
    })
}
  1. promise.race的实现
    const p = Promise.race([p1,p2,p3]);
  • 该方法是将多个Promise实例,包装成一个新的promise实例。
  • 只要p1、p2、p3 之中有一个实例率先改变状态,p的状态就跟着改变,那个率先改变的Promise实例的返回值,就传递给p的回调函数
    static race(promiseList){
        return new MPromise((resolve,reject)=>{
            const length = promiseList.length;
            
            if(length===0){
                return resolve();
            }else{
                for(let i=0;i<length;i++){
                    MPromise.resolve(promiseList[i]).then(
                    (value)=>{
                        return resolve(value);
                    },
                    (reason)=>{
                        return reject(reason);
                    }
                    )
                }
            }
            
        })
    
    }
  1. promise.all的实现
  • promise.all(iterable)方法返回一个promise实例,此实例在iterable参数内所有的promise都完成(resolve),或者参数中不含promise时,回调完成(resolve)
  • 如果参数中promise中有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个promise的结果
    static all(promises){
        return new MPromise((resolve,reject)=>{
            if(!Array.isArray(promises)){
                return reject(new TypeError('arguments must be Array'));
            }
            let count=0;
            let newValues= new Array(promises.length);
            
            for(let i=0;i<promises.length;i++){
                MPromise.resolve(promises[i]).then(
                (value)=>{
                    count++;
                    newValues[i]=value;
                    if(count === promise.length){
                        return resolve(newValues)
                    }
                },
                (reason)=>{
                    return reject(reason);
                }
                )
            }
        
        })
    }

完整代码

const PENDING = 'pending';
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MPromise { 
    constructor(fn) { 
        // 初始状态为pending 
        this.status = PENDING; 
        this.value = null;
        this.reason = null; 
        try {
            fn(this.resolve.bind(this),this.reject.bind(this))
        } catch (e) {
            this.reject(e)
        }
    };
    // 申明的变量
    _status = PENDING;
    FULFILLED_CALLBACK_LIST = []; 
    REJECTED_CALLBACK_LIST = [];
    
    resolve(value) {
        if (this.status === PENDING) { 
            this.value = value;
            this.status = FULFILLED; 
        } 
    };
    reject(reason) { 
        if (this.status === PENDING) {
            this.reason = reason; 
            this.status = REJECTED;
        }
    }
    // 判断是否是函数
    isFunction(param){
        return typeof param==='function';
    };
    
    get status () {
        return this._status;
    }
    
    set status(newStatus){
        this._status = newStatus;
        switch (newStatus){
            case FULFILLED:{
                this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                    callback(this.value);
                });
                break;
            }
            case REJECTED:{
                this.REJECTED_CALLBACK_LIST.forEach(callback => {
                    callback(this.reson);
                });
                break;
            }
        }
    }
    // then方法
    then(onFulfilled,onRejected) {
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : 
        (value) => {
            return value 
         };
        const realOnRejected = this.isFunction(onRejected) ? onRejected : 
        (reason) => { 
            throw reason
        };
        const promise2 = new MPromise((resolve, reject)=> {
            const fulfilledMicrotask = () => {
                queueMicrotask(() => {
                    try{
                        const x = realOnFulfilled(this.value);
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (e){
                        reject(e)
                    }
                })         
            }
            const rejectedMicrotask = () => {
                queueMicrotask(() => {
                    try{
                        const x = realOnRejected(this.reason);
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (e){
                        reject(e)
                    }
                })         
            }

            switch (this.status) {
                case FULFILLED:{
                    fulfilledMicrotask();
                    break;
                }
                case REJECTED:{
                    rejectedMicrotask();
                    break;
                }
                case PENDING:{
                    this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled);
                    this.REJECTED_CALLBACK_LIST.push(realOnRejected);
                }

            }
        });
        return promise2;
    };
    // resolvePromise实现
    resolvePromise(promise2, x, resolve, reject){
        // 如果 newPromise 和 x 指向同⼀对象,以 TypeError 为据因拒绝执⾏ newPromise
        // 这里为了防止死循环
        if(promise2 === x){
            return reject( new TypeError('The promise and the return value are the same'))
        }

        if(x instanceof MPromise){
            // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
            // 也就是继续执⾏x,如果执⾏的时候拿到⼀个y,还要继续解析y
            queueMicrotask(() => {
                x.then((y) => {
                    this.resolvePromise(promise2, y, resolve, reject)
                }, reject)
            })
        } else if (typeof x === 'object' || this.isFunction(x)){
             // 如果 x 为对象或者函数
             if( x===null ){
             // null也会被判断为对象
                 return resolve(x);
             }

            let then =null;

            try {
                // 把 x.then 赋值给 then
                then = x.then;
            } catch (error){
                // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
                return reject(error)
            }

            // 如果 then 是函数
            if(this.isFunction(then)){
                let called = false;
                // 将 x 作为函数的作⽤域 this 调⽤
                // 传递两个回调函数作为参数,第⼀个参数叫做 resolvePromise ,第⼆个参数叫做 rejectPromise
                try {
                    then.call(
                        x,
                        // 如果 resolvePromise 以值 y 为参数被调⽤,则运⾏ resolvePromise
                        (y) => {
                            // 需要有⼀个变量called来保证只调⽤⼀次
                            if(called) return;
                            called = true;
                            this.resolvePromise(promise2, y, resolve, reject);
                        },
                        // 如果 rejectPromise 以据因 r 为参数被调⽤,则以据因 r 拒绝 promise
                        (r) => {
                            if(called) return;
                            called = true;
                            reject(r);
                        }
                    )
                } catch (error){
                    // 如果调用 then 方法就抛出异常 e;
                    if(called) return;
                    // 否则以e为据因拒绝promise
                    reject(error)
                }

            } else {
                // 如果then不是函数,以x 为参数执行promise
                resolve(x)
            }

        } else {
            // 如果 x 不为对象或者函数,以 x 为参数执⾏ promise
            resolve(x)
        }

    }
    
    // catch 方法
    catch (onRejected) {
        return this.then(null, onRejected)
    }
    
    /******----静态方法******/
    
    static resolve (value){
        if(value instanceof MPromise){
            return value;
        }

        return new MPromise((resolve) => {
            resolve(value)
        })
    }
    
    static reject (reason) {
        return new MPromise((resolve,reject)=>{
            reject(reason)
        })
    }
    
    
}