从0手写自己的Promise

2020-09-10  本文已影响0人  Benzic

附带源码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的一个过程,只是自己浅显的见解而已,同时也是看过大神们的解析才能够写出来的结果。写出来也是为了方便自己理解。

上一篇下一篇

猜你喜欢

热点阅读