美文网首页
从0手写自己的Promise

从0手写自己的Promise

作者: Benzic | 来源:发表于2020-09-10 18:13 被阅读0次

    附带源码TS版本:https://github.com/Benzic/Promise/blob/master/Promise.ts
    附带源码JS版本:https://github.com/Benzic/Promise/blob/master/Promise.js
    帮忙点个star吧 谢谢

    这是最后的TypeScript Type先声明在这里
     interface PromiseType {
        $$status: string,                                                          //状态
        failCallBacks: PromiseType[],                                              //成功存放的数组
        successCallBacks: PromiseType[],                                           //失败存放的数组
        result: any,                                                               //成功的值
        error: any,                                                                //失败的原因
        then: (onFulfilled?: any, onRejected?: any) => PromiseType,                             //成功执行方法
        catch: (fn?: Function) => PromiseType,                                     //失败执行的方法
        resolve: (params?: any) => void,                                           //resolve方法
        reject: (params?: any) => void,                                            //reject方法
        resolvePromise: (promise: PromiseType, func: PromiseType, resolve: Function, reject: Function) => void    //链式调用
    }
    
    创建Promise构造函数

    首先创建Promise构造函数,并且为 executor函数 传递参数。Promise一共有三个状态,Pending(等待)、Resolve(成功)、Reject(失败),初始Promise状态为pending。

    export const Promise = function (executor: Function) {
        let _this = this  //'this' implicitly has type 'any' because it does not have a type annotation
        _this.$$status = "pending"
        executor(_this.resolve.bind(this), _this.reject.bind(this));
    } 
    Promise.prototype.resolve = function () {
        if (this.$$status === 'pending') {
            this.$$status = 'success'
        }
    }
    Promise.prototype.reject = function () {
        if (this.$$status === 'pending') {
            this.$$status = 'fail'
        }
    }
    

    在ts下如果直接调用this 会报错,因为typeScript在 noImplicitThis 模式下,不允许this上下文隐式定义。解决方法有很多种,我这里选择的是直接添加this参数

    export const Promise = function (this: PromiseType, executor: Function) {
        let _this: PromiseType = this
        _this.$$status = "pending"
        executor(_this.resolve.bind(this), _this.reject.bind(this));
    } 
    
    Promise.then

    开始为Promise增加then方法,需要这几个对象存放信息,Promise成功的数组successCallBacks、失败数组failCallBacks。

    export const Promise = function (this: PromiseType, executor: Function) {
        let _this: PromiseType = this
        _this.$$status = "pending"
        _this.failCallBacks = [];                                      //失败数组
        _this.successCallBacks = [];                                   //成功数组
        executor(_this.resolve.bind(this), _this.reject.bind(this));
    } 
    Promise.prototype.resolve = function (params: any) {
        if (this.$$status === 'pending') {
            this.$$status = 'success'
            this.successCallBacks[0](params)      //执行第一个成功函数
        }
    }
    Promise.prototype.reject = function (params: any) {
        if (this.$$status === 'pending') {
            this.$$status = 'fail'
            this.failCallBacks[0](params)         //执行第一个失败函数
        }
    }
    Promise.prototype.then = function (onFulfilled: Function, onRejected: Function) {
        this.successCallBacks.push(onFulfilled)         //把成功和失败的方法分别添加到对应数组当中
        this.failCallBacks.push(onRejected)
    }
    

    Promise有一个叫做then的方法,里面有两个参数:onFulfilled,onRejected,成功有成功的值,失败有失败的原因。一个简单的拥有then方法的Promise方法就行了,但是在ts中调用却会报错,报错如下:

    new Promise(function (resolve: Function, reject: Function) {   //'new' expression, whose target lacks a construct signature, implicitly has an 'any' type
        setTimeout(() => {
            resolve("test promise")
        }, 1000);
    }).then((res: any) => {
        console.log(res, 'then result')
    })
    

    解决方法也很简单只需要在Promise 函数后面 加上 as any即可 输出正常结果;

      export const Promise = function (this: PromiseType, executor: Function) {
            ...
      } as any
    
    链式调用

    Promise真正的精髓就是它的链式调用,所以不能链式调用的Promise函数那不叫Promise函数。

    1. 然而实际上不管是 then 还是 catch 方法调用,都返回了一个新的promise对象。为了达到链式,在then里面返回一个新的promise,称为promise2: promise2 = new Promise((resolve, reject)=>{})
      •将这个promise2返回的值传递到下一个then中
      •如果返回一个普通的值,则将普通的值传递给下一个then中
    2. 当我们在第一个then中 return 了一个参数(参数未知,需判断)。这个return出来的新的promise就是onFulfilled()或onRejected()的值,这里就需要一个resolvePromise来处理。
    Promise.prototype.resolvePromise = function (promise: PromiseType, func: PromiseType, resolve: Function, reject: Function) {
        let _this = this
        if (func === promise) {  //避免循环引用
            return reject("error")
        }
        let called: any              //防止多次调用
        if (func && (typeof func === 'object' || typeof func === 'function')) {
            try {
                let then = func.then;
                if (typeof then === 'function') {
                    //失败方法和成功方法只执行一次
                    then.call(func, (func2: PromiseType) => {
                        if (called) return;
                        called = true;
                        //返回新的promise,继续处理
                        _this.resolvePromise(promise, func2, resolve, reject);
                    }, (error: any) => {
                        if (called) return;
                        called = true;
                        reject(error);
                    })
                } else {        //直接输出成功
                    resolve(func);
                }
            } catch (error) {  //直接输出失败
                if (called) return;
                called = true;
                reject(error)
            }
        } else {
            resolve(func);
        }
    }
    

    改造resolve、reject方法,循环执行then方法

    Promise.prototype.resolve = function (params: any) {
        if (this.$$status === "pending") {
            this.$$status = "success"
            this.result = params;
            if (!this.successCallBacks.length) return;
            this.successCallBacks.map((a: Function) => a())
        }
    }
    Promise.prototype.reject = function (params: any) {
        if (this.$$status === "pending") {
            this.$$status = "fail"
            this.error = params
            if (!this.failCallBacks.length) return;
            this.failCallBacks.map((a: Function) => a())
        }
    }
    

    改造then方法和Promise构造函数,异步执行

    export const Promise = function (this: PromiseType, executor: Function) {
       ...
       setTimeout(() => {
            try {
                executor(_this.resolve.bind(this), _this.reject.bind(this));
            } catch (error) {
                _this.reject.bind(this)(error)
            }
        });
    } as any
    Promise.prototype.then = function (onFulfilled: Function, onRejected: Function) {
        let _this = this;
        let newPromise = new Promise((resolve: Function, reject: Function) => {
            if (_this.$$status === "success") {
                setTimeout(() => {
                    try {
                        let func = onFulfilled(_this.result);
                        _this.resolvePromise(newPromise, func, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            if (_this.$$status === 'fail') {
                setTimeout(() => {
                    try {
                        let func = onRejected(_this.error);
                        _this.resolvePromise(newPromise, func, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            if (_this.$$status === 'pending') {
                _this.successCallBacks.push(() => {
                    setTimeout(() => {
                        try {
                            let func = onFulfilled(_this.result);
                            _this.resolvePromise(newPromise, func, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    })
                })
                _this.failCallBacks.push(() => {
                    setTimeout(() => {
                        try {
                            let func = onRejected(_this.error);
                            _this.resolvePromise(newPromise, func, resolve, reject);
                        } catch (error) {
                            reject(error)
                        }
                    })
                })
            }
        })
        return newPromise
    }
    

    试试结果

    new Promise(function (resolve: Function, reject: Function) {
        setTimeout(() => {
            resolve("test promise")
        }, 1000);
    }).then((res: any) => {
        console.log(res, 'then result')
        return new Promise((resolve: Function, reject: Function) => {
            resolve("test promise2")
        })
    }).then((res: any) => {
        console.log(res, 'then result')
    }).then((res: any) => {
        console.log(res, 'then result')
    })
    
    链式调用
    Promise.catch

    接下来轮到Promise中的错误处理了,Promise.catch方法就变得非常简单的了,毕竟前面铺垫那么多代码,只需要三行代码即可。

    Promise.prototype.catch = function (fn: Function) {
        return this.then(null, fn) //直接把该方法放进failCallBacks数组
    }
    

    试试结果

    new Promise(function (resolve: Function, reject: Function) {
        setTimeout(() => {
            resolve("test promise")
        }, 1000);
    }).then((res: any) => {
        console.log(res, 'then result')
        return new Promise((resolve: Function, reject: Function) => {
            resolve("test promise2")
        })
    }).then((res: any) => {
        console.log(res, 'then result')
    }).then((res: any) => {
        console.log(res, 'then result')
        throw Error("test error")
    }).catch((error: any) => {
        console.log(error, 'catch error')
    })
    
    Promise.catch
    Promise.resolve

    Promise.resolve和reject的方法就非常简单了

    Promise.resolve = function (val: any) {
        if (val instanceof Promise) {
            return val
        }
        return new Promise((resolve: Function, reject: Function) => {
            resolve(val)
        })
    }
    
    Promise.reject
    Promise.reject = function (val: any) {
        if (val instanceof Promise) {
            return val
        }
        return new Promise((resolve: Function, reject: Function) => {
            reject(val)
        })
    }
    
    Promise.race
    Promise.race = function (promises: any) {
        if (!(promises instanceof Array)) {
            throw new TypeError("parameter must be array")
        }
        let flag: boolean;      //避免执行多次
        return new Promise((resolve: Function, reject: Function) => {
            promises.forEach((item: any) => {
                if (item instanceof Promise) {
                    item.then((res: any) => {
                        if (!flag) {
                            flag = true;
                            resolve(res)
                        }
                    }).catch((error: any) => {
                        if (!flag) {
                            flag = true;
                            reject(error)
                        }
                    })
                } else {
                    if (!flag) {
                        flag = true;
                        resolve(item)
                    }
                }
            })
        })
    }
    

    试试结果

    const promise1 = new Promise((resolve: any, reject: any) => {
        setTimeout(() => {
            resolve('promise1')
        }, 5000);
    })
    const promise2 = new Promise((resolve: any, reject: any) => {
        setTimeout(() => {
            resolve('promise2')
        }, 10000);
    })
    const promise3 = Promise.resolve("promise3")
    const promise4 = 'promise4'
    
    Promise.race([promise1, promise2, promise3, promise4]).then((res: any) => {
        console.log(res)
    })
    
    Promise.race

    因为Promise4只是字符串所以最先执行输出

    Promise.all

    all就是在race的基础上改进一下即可,不着急返回结果,先把结果保存下来,等最后一个promise执行完毕后再统一输出,所以:

    Promise.all = function (promises: PromiseType) {
        if (!(promises instanceof Array)) {
            throw new TypeError("parameter must be array")
        }
        let count = 0//用于计数,当等于len时就resolve
        let len = promises.length
        let result: any = []//用于存放结果
        return new Promise((resolve: Function, reject: Function) => {
            if (!promises.length) {
                resolve(result)
            } else {
                promises.forEach((item: any) => {
                    if (item instanceof Promise) {
                        item.then((data: any) => {
                            result[count++] = data
                            if (count === len) {
                                resolve(result)
                            }
                        }).catch((error: any) => {
                            reject(error)
                        })
                    } else {
                        result[count++] = item
                        if (count === len) {
                            resolve(result)
                        }
                    }
                });
            }
    
        })
    }
    

    试试结果

    const promise1 = new Promise((resolve: any, reject: any) => {
        setTimeout(() => {
            resolve('promise1')
        }, 5000);
    })
    const promise2 = new Promise((resolve: any, reject: any) => {
        setTimeout(() => {
            resolve('promise2')
        }, 10000);
    })
    const promise3 = Promise.resolve("promise3")
    const promise4 = 'promise4'
    Promise.all([promise1, promise2, promise3, promise4]).then((res: any) => {
        console.log(res)
    })
    
    Promise.all

    按着执行顺序输出结果,大功告成

    所以这就是从0编写Promise的一个过程,只是自己浅显的见解而已,同时也是看过大神们的解析才能够写出来的结果。写出来也是为了方便自己理解。

    相关文章

      网友评论

          本文标题:从0手写自己的Promise

          本文链接:https://www.haomeiwen.com/subject/vvupektx.html