highlight: a11y-dark
theme: healer-readable

手写promise思路

先逐步分析,一步一步的写代码,最终有完整示例

1.promise是一个类,参数是一个函数执行器,且会立即执行。函数执行器接受两个参数,一个成功回调resolve,一个失败回调reject

class MyPromise {
    constructor(executor) {
        executor(this.resolve.bind(this), this.reject.bind(this));
    }
    resolve(value) {}
    reject(reason) {}
}

2.promise有3中状态pending,fulfilled,rejected,初始状态为pending,当promise的状态一经改变则无法再修改

//  在外部定义常量值
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const PENDING = 'pending';
......
constructor(executor) {
    //  设置当前promise的状态值
    this.status = PENDING;
    executor(this.resolve.bind(this), this.reject.bind(this));
}
......

3.resolve/reject方法就是修改当前promise的状态的

......
resolve(value) {
    //  如果当前状态不是初始状态,则不能被修改
    if (this.status !== PENDING) return;
    //  将当前状态修改为成功
    this.status = FULFILLED;
}
......
reject(reason) {
    //  如果当前状态不是初始状态,则不能被修改
    if (this.status !== PENDING) return;
    //  将当前状态修改为失败
    this.status = REJECTED;
}

4.promise有一个then方法,它有两个参数,第一个参数是promise成功的回调,第二个参数是失败的回调

/*   因为successCallback和failCallback接受resolve和reject中的参数,
所需要记录成功的值和失败的值
*/
......
constructor(executor) {
    ......
    //  记录成功时返回的值
    this.value = undefined;
    //  记录失败时返回的值
    this.reason = undefined;
    ......
}
......
resolve(value) {
    ......
    //  记录成功时返回的值
    this.value = value;
}
......
reject(reason) {
    ......
    //  记录失败时返回的值
    this.reason = reason;
}
......
then(successCallback, failCallback) {
     if (this.status === FULFILLED) {
         successCallback(this.value)
     } else if (this.status === REJECTED) {
         failCallback(this.reason)
     }
}
......

5.当函数执行器是同步的时候,promise的状态会马上被改变,所以then能接收到状态处理成功和失败,但是当函数执行器是异步的时候,promise的状态是等待状态,这时候就需要记录成功和失败的回调,等待异步执行器执行完毕后在调对应的回调函数

/*  所以我们需要两个变量来存贮then方法的成功回调和失败回调,当异步执行
器执行完毕后,resolve/reject分别执行存储的成功回调和失败回调
*/
......
constructor(executor) {
    ......
    //  记录成功回调
    this.successCallback = undefined;
    //  记录失败回调
    this.failCallback = undefined;
    ......
}
......
then(successCallback, failCallback) {
    ......
    else if (this.status === PENDING) {
        this.successCallback = successCallback;
        this.failCallback = failCallback;
    }
}
......
resolve(value) {
    ......
    //  当执行器为异步时,执行器执行完毕,需要执行成功回调
    this.successCallback(this.value);
}
......
reject(reason) {
    ......
    //  当执行器为异步时,执行器执行完毕,需要执行失败回调
    this.failCallback(this.reason);
}
......

6.一个promise可以同时执行多个then方法,且这些then方法的回调都会执行,当函数执行器为同步的时候,我们并不需要做处理,但是当函数执行器为异步时,我们需要把每个then方法的回调函数都保存起来,当函数执行器执行完毕的时候,再依次调用,所以,存储成功回调和失败回调是数组形式

//  存储成功回调和失败回调的变量都是数组
......
constructor(executor) {
    ......
    //  记录成功回调
    this.successCallback = [];
    //  记录失败回调
    this.failCallback = [];
    ......
}
......
then(successCallback, failCallback) {
    ......
    else if (this.status === PENDING) {
        this.successCallback.push(successCallback);
        this.failCallback.push(failCallback);
    }
}
......
resolve(value) {
    ......
    //  当执行器为异步时,执行器执行完毕,需要执行成功回调
    while (this.successCallback.length)this.successCallback.shift()(this.value);
}
......
reject(reason) {
    ......
    //  当执行器为异步时,执行器执行完毕,需要执行失败回调
    while (this.failCallback.length) this.failCallback.shift()(this.reason);
}
......
    

7.promise可以链式调用then方法,意思就是then方法也要返回一个promise就可以链式调用then了,第二个then是第一个then中返回的promise对象的调用,这样API可以做到连续传递参数,且不需要回到地狱。如果在第一个then中的成功回调中返回一个普通值,那么下一个Then将接收到这个值,如果是个promise,那下一个then将是等待这个promise执行完毕,之后将值传递下去

then(successCallback, failCallback) {
     /*  判断当前的successCallback/failCallback的返回值是否是异步的,
     如果异步则需要等到异步完成
     */
     const resolvePromise = (x, resolve, reject) => {
        if (x instanceof MyPromise) {
            x.then(resolve, reject);
        } else {
            resolve(x);
        }
     }
     const promise2 = new MyPromise((resolve, reject) => {
         if (this.status === FULFILLED) {
             const x = successCallback(this.value)
             resolvePromise(x, resolve, reject);
         } else if (this.status === REJECTED) {
             const x = failCallback(this.reason);
             resolvePromise(x, resolve, reject)
         } else if (this.status === PENDING) {
            this.successCallback.push(() => {
                const x = successCallback(this.value);
                resolvePromise(promise2, x, resolve, reject)
            });
            this.failCallback.push(() => {
                const x = failCallback(this.reason)
                resolvePromise(promise2, x, resolve, reject)
            });
         }
     })
     return promise2;
}
......
//  不需要传递this.value/this.reason参数了
resolve(value) {
    ......
    //  当执行器为异步时,执行器执行完毕,需要执行成功回调
    while (this.successCallback.length) this.successCallback.shift()();
}
......
reject(reason) {
    ......
    //  当执行器为异步时,执行器执行完毕,需要执行失败回调
    while (this.failCallback.length) this.failCallback.shift()();
}
......

8.在then方法中是不能返回自己的promose对象,不然会发生循环调用

/*  我们把then方法修改如下,这里想直接判断x和promise2是否相等做不到,因为这时候
还没有promise2,所以要加一个定时器,把回调变成异步的
*/
then(successCallback, failCallback) {
     /*  判断当前的successCallback/failCallback的返回值是否是异步的,如果异步
     则需要等到异步完成
     */
     const resolvePromise = (promise, x, resolve, reject) => {
        if (promise === x) {
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        if (x instanceof MyPromise) {
            x.then(resolve, reject);
        } else {
            resolve(x);
        }
     }
     const promise2 = new MyPromise((resolve, reject) => {
         if (this.status === FULFILLED) {
             setTimeout(() => {
                 const x = successCallback(this.value)
                 resolvePromise(promise2, x, resolve, reject);             
             }, 0)
         } else if (this.status === REJECTED) {
             setTimeout(() => {
                 const x = failCallback(this.reason);
                 resolvePromise(promise2, x, resolve, reject)
             }, 0)
         } else if (this.status === PENDING) {
            this.successCallback.push(() => {
                setTimeout(() => {
                    const x = successCallback(this.value);
                    resolvePromise(promise2, x, resolve, reject)
                }, 0)
            });
            this.failCallback.push(() => {
                setTimeout(() => {
                    const x = failCallback(this.reason)
                    resolvePromise(promise2, x, resolve, reject)
                }, 0)
            });
         }
     })
     return promise2;
}

9.错误处理,函数执行器出错时,要执行reject回调,当then方法执行错误时,也要执行reject回调,这个reject是在下一次then方法的失败回调中抓取,采用tryCatch的方式抓取错误

//  首先抓取执行器的错误
constructor(executor) {
    ......
    //  执行器,会立即执行,它接受两个参数,一个成功的回调,一个失败的回调
    try {
        executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
        this.reject(error)
    }
}

//  抓取then中的错误
then(successCallback, failCallback) {
     /*  判断当前的successCallback/failCallback的返回值是否是异步的,如果异步
     则需要等到异步完成
     */
     const resolvePromise = (promise, x, resolve, reject) => {
        if (promise === x) {
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        if (x instanceof MyPromise) {
            x.then(resolve, reject);
        } else {
            resolve(x);
        }
     }
     const promise2 = new MyPromise((resolve, reject) => {
         if (this.status === FULFILLED) {
             setTimeout(() => {
                 try {
                     const x = successCallback(this.value)
                     resolvePromise(promise2, x, resolve, reject);                    
                 } catch (error) {
                     reject(error)
                 }
             }, 0)
         } else if (this.status === REJECTED) {
             setTimeout(() => {
                 try {
                     const x = failCallback(this.reason);
                     resolvePromise(promise2, x, resolve, reject)                
                 } catch (error) {
                     reject(error)
                 }
             }, 0)
         } else if (this.status === PENDING) {
            this.successCallback.push(() => {
                setTimeout(() => {
                     try {
                        const x = successCallback(this.value);
                        resolvePromise(promise2, x, resolve, reject)            
                     } catch (error) {
                         reject(error)
                     }
                }, 0)
            });
            this.failCallback.push(() => {
                setTimeout(() => {
                     try {
                        const x = failCallback(this.reason)
                        resolvePromise(promise2, x, resolve, reject)        
                     } catch (error) {
                         reject(error)
                     }
                }, 0)
            });
         }
     })
     return promise2;
}

10.then方法的参数是可选参数,当resolve回调不传递时,其实是相当于传递了一个返回值的方法 ,then方法的两个参数得判断类型,当输入为函数的时候才会向下传递,如果是普通值则忽略

then(successCallback, failCallback) {
    //  参数变成可选的,当不传递参数时相当于传递了类似的函数
    //  这个地方要加上判断是不是函数类型,如果是普通值则忽略掉
    if (successCallback) {
        if (typeof successCallback !== 'function') {
            successCallback = value => value;
        }
    } else {
        successCallback = value => value;
    }
    if (failCallback) {
        if (typeof failCallback !== 'function') {
            reason => { throw reason }
        }
    } else {
        reason => { throw reason }
    }
    ......
}

11.promise.all是一个静态方法,可以执行多个promise,以及普通值

static all(array) {
    const result = [];
    let i = 0;
    return new MyPromise((resolve, reject) => {
        const addData = (key, value) => {
            result[key] = value;
            i++;
            if (i === array.length) {
                resolve(result);
            }
        }
        array.forEach((item, index) => {
            if (item instanceof MyPromise) {
                item.then(value => addData(index, value), error => reject(error))
            } else {
                addData(index, item);
            }
        })
    })
}

12.promise.resolve是一个静态方法,传递一个参数,如果传递的是一个普通参数,则将转换为promise返回,如果参数是promise,则直接返回

static resolve(item) {
    if (item instanceof MyPromise) {
        return item;
    } else {
        return new MyPromise(resolve => {
            resolve(item)
        })
    }
}

13.promise.reject方法传递一个参数,返回一个promise,将参数向下传递进then方法的第二个回调函数中

static reject(error) {
    return new MyPromise((resolve, reject) => {
        reject(error)
    })
    
}

14.finally方法,不管promise是成功开始失败,都会执行finally方法一次,finally后可以链式调用then方法拿到该promise的结果,如果finally方法返回了一个promise则后面的then需要等待

finally(callback) {
    return this.then(value => {
        const x = callback();
        return MyPromise.resolve(x).then(() => value)
    }, error => {
        const x = callback();
        return MyPromise.reject(x).then(undefined, () => error)
    })
}

15.catch方法,抓取错误,他就相当于执行了then中的失败回调

catch(callback) {
    this.then(undefined, callback);
}

promise完整示例

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

/*  这个我用了平常的定义变量的方式,变量都在constructor中,这里就需要resolve和reject bind this了
也可以用声明式写在constructor外
*/

class MyPromise {
    constructor(executor) {
        //  设置当前promise的状态值
        this.status = PENDING;
        //  记录成功时返回的值
        this.value = undefined;
        //  记录失败时返回的值
        this.reason = undefined;
        //  记录成功回调
        this.successCallback = [];
        //  记录失败回调
        this.failCallback = [];

        //  执行器,会立即执行,它接受两个参数,一个成功的回调,一个失败的回调
        try {
            executor(this.resolve.bind(this), this.reject.bind(this));
        } catch (error) {
            this.reject(error)
        }
    }

    resolve(value) {
        //  如果当前状态不是初始状态,则不能被修改
        if (this.status !== PENDING) return;
        //  将当前状态修改为成功
        this.status = FULFILLED;
        //  记录成功时返回的值
        this.value = value;
        //  当执行器为异步时,执行器执行完毕,需要执行成功回调
        while (this.successCallback.length) this.successCallback.shift()();
    }

    reject(reason) {
        //  如果当前状态不是初始状态,则不能被修改
        if (this.status !== PENDING) return;
        //  将当前状态修改为失败
        this.status = REJECTED;
        //  记录失败时返回的值
        this.reason = reason;
        //  当执行器为异步时,执行器执行完毕,需要执行失败回调
        while (this.failCallback.length) this.failCallback.shift()();
    }

    then(successCallback, failCallback) {
        //  参数变成可选的,当不传递参数时相当于传递了类似的函数
        //  这个地方要加上判断是不是函数类型,如果是普通值则忽略掉
        if (successCallback) {
            if (typeof successCallback !== 'function') {
                successCallback = value => value;
            }
        } else {
            successCallback = value => value;
        }
        if (failCallback) {
            if (typeof failCallback !== 'function') {
                reason => { throw reason }
            }
        } else {
            reason => { throw reason }
        }
        const resolvePromise = (promise2, x, resolve, reject) => {
            if (promise2 === x) {
                return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
            }
            if (x instanceof MyPromise) {
                x.then(resolve, reject);
            } else {
                resolve(x);
            }
        }
        const promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        const x = successCallback(this.value);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        const x = failCallback(this.reason);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else if (this.status === PENDING) {
                //  当执行器为异步的时候,需要记录回调函数,等待执行器执行完毕后在执行回调函数
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            const x = successCallback(this.value);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                });
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            const x = failCallback(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                });
            }
        })
        return promise2;
    }

    finally(callback) {
        return this.then(value => {
            const x = callback();
            return MyPromise.resolve(x).then(() => value)
        }, error => {
            const x = callback();
            return MyPromise.reject(x).then(undefined, () => error)
        })
    }

    catch(callback) {
        this.then(undefined, callback);
    }

    static all(array) {
        const result = [];
        let i = 0;
        return new MyPromise((resolve, reject) => {
            const addData = (key, value) => {
                result[key] = value;
                i++;
                if (i === array.length) {
                    resolve(result);
                }
            }
            array.forEach((item, index) => {
                if (item instanceof MyPromise) {
                    item.then(value => addData(index, value), error => reject(error))
                } else {
                    addData(index, item);
                }
            })
        })
    }

    static resolve(item) {
        if (item instanceof MyPromise) {
            return item;
        } else {
            return new MyPromise(resolve => {
                resolve(item)
            })
        }
    }

    static reject(error) {
        return new MyPromise((resolve, reject) => {
            reject(error)
        })
    }
}

版权声明:本文为qq_35258995原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_35258995/article/details/120719661