180行代码手动实现一个promise

本文深入解析Promise的工作原理,从实例化到then方法的执行流程,包括状态管理、错误处理及异步操作。涵盖Promise.all、Promise.race等高级功能。
function MyPromise(fn) {
    // 缓存当前的this,方便引用
    let promise = this;

    // 初始化当前实例的value,error,处理成功事件的arr,处理失败事件的arr
    // 初始化当前状态,默认为pending
    promise.value = null;
    promise.error = null;
    promise.successArr = [];
    promise.errorArr = [];
    promise.status = 'pending';

    // 必须通过new来调用
    if (this instanceof MyPromise === false) {
        return new MyPromise(fn)
    }

    // 定义当前的resolve方法
    const resolve = function (val) {
        // 确保在then方法执行之后再执行resolve
        setTimeout(function () {
            promise.value = val;
            // 判断是否已经改变了状态
            if (promise.status !== "pending") {
                console.log("状态已经为error,不能再更新了");
                return
            }
            promise.status = "success";
            promise.successArr.forEach(function (callback) {
                promise.value = callback(promise.value)
            })
        }, 0)
    };
    const reject = function (err) {

        // 确保在then方法执行之后再执行reject

        setTimeout(function () {
            promise.error = err;
            if (promise.status !== "pending") {
                console.log("状态已经为success,不能再更新了");
                return
            }
            promise.status = "error";
            promise.errorArr.forEach(function (callback) {
                promise.error = callback(promise.error)
            })
        }, 0)
    };

    // 捕获执行过程中的错误,并reject出去
    try {
        // 将resolve跟reject方法传进回调函数
        fn(resolve, reject)
    } catch (e) {
        reject(e)
    }
}
// 定义原型上的方法
MyPromise.prototype = {
    then: function (successEvent, errorEvent) {
        // 缓存当前this
        let promise = this;
        return new MyPromise(function (resolve, reject) {

            // 包装then中的成功回调函数
            // 为什么要包装?因为可以处理返回值,
            // 并且在得到返回值后再进行resolve,从而保证下一个then方法按顺序执行
            const successHandler = function (value) {
                // 将当前的函数运行结果传给ret
                let ret = typeof successEvent === "function" && successEvent(value);
                // 如果返回的是一个promise对象,则执行then方法,并将返回的value值,resolve到当前promise
                if (!!ret && typeof ret['then'] === "function") {
                    ret.then(function (value) {
                        resolve(value)
                    }, function (error) {
                        reject(error)
                    })
                } else {
                    resolve(ret)
                }
            };

            // 包装then中的失败回调函数
            const errorHandler = function (error) {
                if( typeof errorEvent === "function"){
                    let ret = errorEvent(error);
                    resolve(ret);
                }else{
                    reject(error)
                }
            };

            // 判断上一级promise的状态
            if (promise.status === "pending") {
                // 将成功的处理函数,失败的处理函数分别推进数组里
                promise.successArr.push(successHandler);
                promise.errorArr.push(errorHandler)
            } else if (promise.status === "success") {
                successHandler(promise.value)
            } else if (promise.status === "error") {
                errorHandler(promise.error)
            }
        })
    },

    // 捕获错误
    catch: function (errorEvent) {
        return this.then(function (value) {
            return value
        }, errorEvent)
    },
    // 当全部的promise成功时,返回一个包含所有结果的数组,当有一个失败时,则返回错误的原因
    all: function (arr) {
        if (!(arr instanceof Array) || arr.length === 0) {
            console.error("当前传入的需要数组而且不是空数组");
            return
        }
        return new MyPromise(function (resolve, reject) {
            let length = arr.length,
                result = [];
            arr.forEach(function (promise, index) {
                promise.then(function (value) {
                    result.push(value);
                    if (index === length - 1) {
                        resolve(result)
                    }
                }, function (error) {
                    console.log("该死,某一项出错了");
                    reject(error)
                })
            })
        })
    },
    // 有一个promise变化,则变化
    race: function (arr) {
        if (!(arr instanceof Array) || arr.length === 0) {
            console.error("当前传入的必须为数组而且不能是空数组");
            return
        }
        return new MyPromise(function (resolve, reject) {
            arr.forEach(function (promise) {
                promise.then(function (value) {
                    resolve(value)
                }, function (err) {
                    reject(err)
                })
            })
        })
    },
    // 延迟函数
    delay: function (value, ms) {
        return this.then(function (val) {
            return new MyPromise(function (resolve, reject) {
                setTimeout(function () {
                    resolve(value || val)
                }, ms)
            })
        })
    }
};

// 改变当前的constructor指向,并设为不可枚举
Object.defineProperty(MyPromise.prototype, "constructor", {
    writable: true,
    enumerable: false,
    configurable: true,
    value: MyPromise
});

// 快速新建一个Promise对象
MyPromise.resolve = function (arg) {
    if (typeof arg === "object" && typeof arg['then'] === "function") {
        return arg
    } else {
        return new MyPromise(function (resolve, reject) {
            resolve(arg)
        })
    }
};
MyPromise.reject = function (arg) {

    return new MyPromise(function (resolve, reject) {
        reject(arg)
    })
};

// 测试内容
new MyPromise(function (resolve, reject) {
    setTimeout(function () {
        resolve("正确");
    }, 1000);
}).then(function (res) {
    console.log(res);
    return MyPromise.resolve(333)
}).then(function (res) {
    console.log(res);
    // return MyPromise.reject("出错了!")
}).then(function (res) {
    console.log(res);
    return MyPromise.reject("出错了")
}).catch(function (err) {
    console.log(err);
    return 11
}).then(function (res) {
    console.log(res);
}).then(function (e) {
    console.log(e);
    return "test"
}).catch(function (e) {
    console.log(e);
    return "555"
}).then(function (res) {
    console.log(res);
    return  MyPromise.reject("大猪头")
}).then(function (res) {

}).then(function () {

},function (err) {
    setTimeout(function () {
        console.log(err);
    },1000)
})

 

思路是这样的:

第一,实例化promise A需要接受一个回调函数,我们在初始化时,缓存当前的this,然后定义成功回调函数数组,失败回调函数数组等,最后将定义好的resolve与reject传入回调函数(此处要注意利用setTimeOut,将resolve函数延后执行),回调函数要保持状态单一性,一旦pending改变,就无法再更改状态;

第二,在原型上实现then方法:then方法是最难理解的:首先,为了链式调用,需要返回一个新的promise实例B;其次,为了在执行完then中的回调函数后,能将返回值传给下一个promise实例C,需要将then方法传入的两个回调函数重新包装成新的函数;接下来,就得判断上一级promiseA的状态了,如果是“pending”,就将包装好的函数分别存在上一级数组里,但如果是“success”或者“error”,证明上一级的状态已经被resolve或者reject了,就传入当前实例上的value或error属性(具体看你成功还是失败)到对应的包装函数上,并执行;举个例子,假设promiseA在延迟3秒后执行resolve的方法,就会将当前的会函数数组的回调函数取出来执行,执行完后,回调函数内部调用了promiseB的resolve,如果执行了then方法,就将状态传给promiseC;then方法的细节有很多,希望能仔细研究;

第三,catch方法:catch方法只需要调用this.then就可以了;catch方法在没有捕获到错误时,只会将上一级传递的参数传递到下一级,如果捕获到,则下一级then也能正常运行并接受catch方法的返回值;则需要注意一点是,不能直接在第一个参数传undefined,目前还没有优化;

第四,Promise.all跟Promise.race方法,跟原生差不多,按照功能实现就可以了;

第五,Promise.resolve,Promise.reject,快速生成一个Promise对象,直接调用then即可用;

就这么多了,爆肝研究原理并且处理各种bug,终于跟原生的差不多了,还有一个小细节,就是Promise对吞并错误,但是我还没实现,你不知道的JavaScript的作者提到,promise吞并错误往往会造成严重的调试时的困难,所以就不进行处理了;

 

参考:手把手教你实现一个完整的 Promise 原文有少少瑕疵,但是足够详细,大家可以去参考下~

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值