手写一个Promise源码

126 阅读2分钟

请先了解Promise的使用方法,再阅读本文,能坚持到最后才能有所收获。

Promise到底做了什么?

Promise状态只转换一次

问题: 我们知道Promise有三种状态,分别是pending/reslove/reject,一旦从pending转成resolve或reject,即成功了以后即便再执行reject('failed')状态也不会改变

    function excuteOnce(then) {
        var runs = 0;
        function run(fn) {
            //1. new Promise 第一步 限制次数
            return function (value) {
                if(runs++ > 0) return 
                fn(value)
            }
        }
        then(
            run(function() { alert('success') }),
            run(function() { alert('error') })
        )
   }
    
    //executor是 new Promise((resolve, reject) => {})这个参数
   excuteOnce(executor)

解决问题:我们使用闭包来访问外部的变量就可以解决只执行一次的问题

resolve和reject为什么可以传参到then

var self = this, resolvers = [], rejecters = [], resolveCurrent = handle(resolvers, true), rejectCurrent = handle(rejecters, false)
var instance = self._instance = { resolvers: resolvers, rejecters: rejecters }

function handle(list, shouldAbsorb) {
    return function execute(value) {
        try {
            setTimeout(function() {
                //执行异步操作 -> 执行回调函数队列 -> 重置队列
                for(var i = 0; i < list.length; i++) list[i](value)
                resolvers.length = rejecters.length = 0
                instance.state = shouldAbsorb 
                
                //then就是通过这个闭包来访问上一个数据的
                //当然他必须先调用resolve和reject回调函数
                instance.retry = function() { execute(value) }
            })
        }catch(e) {
            rejectCurrent(e)
        }
    }
}

function executeOnce(then) {
    //以上省略代码...
    var onerror = run(rejectCurrent)
    try { then(run(resolveCurrent), onerror) } catch(e) { onerror(e) }
}

new Promise通过定义一个retry回调函数来传参

then为什么可以链式调用

PromisePolyfill.prototype.then = function(onFulfilled, onRejection) {
    //创建一个新的Promise作为下一个返回值,所以可以链式调用
    var resolveNext, rejectNext
    var promise = new PromisePolyfill(function(resolve, reject){ resolveNext = resolve, rejectNext = reject })
    return promise
}

我们提供一个全新的Promise作为返回值,所以可以链式调用

then如何获取上一个Promise传入的数据?

PromisePolyfill.prototype.then = function(onFulfilled, onRejection) {
    var self = this
    function handle(callback, list, next, state) {
        //2. 第二步,入队
        list.push(function(value) { //retry回调函数传过来的值
            //传入下一个Promise
            try { resolveNext(callback(value)) } catch(e) { if(rejectNext) rejectNext(value) }
        })
        //3. 第三步,执行retry,他会执行retry -> retry会执行list队列
        if(typeof instance.retry === "function" && instance.state === state) instance.retry()
    }

    //省略代码...
    handle(onFulfilled, self.resolvers, resolveNext, true)
    handle(onRejection, self.rejecters, rejectNext, false)
}
  • 第一步:list.push添加一个回调函数
  • 第二步:执行之前的retry回调函数,retry通过闭包保存之前的变量以及作用域
  • 第三步:重新执行execute,执行list队列,重新定义retry函数给下一个then来调用
  • 第四步:list队列执行回调函数,传入新的Promise(resolve(1))

总结

以上手写一个Promise,可能会有考虑不周的情况出现,还有一种情况没有考虑进去

  • then(() => new Promise((resolve, reject) => resolve(1)))