Promise源码实现

更新日期: 2022-03-24阅读: 1k标签: 源码

定义Promise的构造函数,因为创建Promise对象的时候会接收一个函数executor,并且函数会立即被调用,executor函数接收两个函数方法,resolve和reject。调用resolve和reject的时候会传入对应的值。

function Promise (executor) {
    function resolve(value) {

    }
    function reject(reason) {

    }
    excutor(resolve, reject);
}

Promise存在3种状态的,等待pending,成功resolved以及失败rejected,状态只可以改变一次,默认是等待状态,可以通过status属性来保存状态,默认pending。状态只可改变一次,只有当状态未发生改变时才去改变状态。成功或失败会传递参数,定义value和reason分别保存。

function Promise (executor) {
    var self = this;

    self.status = 'pending';
    self.value;
    self.reason;

    function resolve(value) {
        if (self.status === 'pending') {
            self.status = 'resolved';
            self.value = value;
        }

    }
    function reject(reason) {
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
        }
    }
    excutor(resolve, reject);
}

Promise存在then方法,接收两个参数,成功的回调onFulfilled和失败的回调onRejected,方法需要添加在原型上。

Primsie.prototype.then = function(onFulfilled, onRejected) {
    var self = this;
    if (self.status === 'resolved') {
        onFulfilled(self.value);
    }

    if (self.status === 'rejected') {
        onRejected(self.reason);
    }
}

1. Promise A+规范

Promise的概念不是凭空出现的,是Promise A+规范中定义的,要求所有实现Promise的代码都必须要基于这个规范。

1.1 “promise”是一个具有then方法的对象或函数,其行为符合此规范。也就是说Promise是一个对象或者函数。

1.2 “thenable”是一个定义then方法的对象或函数,说句人话也就是这个对象必须要拥有then方法。

1.3 “value”是任何合法的JavaScript值(包括undefined、或者promise)。

1.4 promise中的异常需要使用throw语句抛出。

1.5 当promise失败的时候需要给出失败的原因。

1.1 promise必须要拥有三个状态: pending、fulfilled 和 rejected。

1.2 当promise的状态是pending时,他可以变为成功fulfilled或者失败rejected。

1.3 如果promise是成功状态,则他不能转换成任何状态,而且需要一个成功的值,并且这个值不能被改变。

1.4 如果promise是失败状态,则他不能转换成任何状态,而且需要一个失败的原因,并且这个值不能被改变。

Promise需要支持异步逻辑,当Promise函数中异步调用resolve的时候,then方法不会执行。因为then方法执行的时候resolve并没有执行,也就是Promise的状态还未变化。需要改造Promise代码。当调用then方法的时候可能还是pending状态,这个时候应该把onFulfilled和onRejected先存起来,当执行了resolve或者reject的时候再执行onFulfilled或onRejected。这里需要定义两个变量,分别存储onFulfilled和onRejected。

function Promise (executor) {
    var self = this;

    self.status = 'pending';
    self.value;
    self.reason;
    self.onResolvedCallbacks = []; // 存放所有成功的回调。
    self.onRejectedCallbacks = []; // 存放所有失败的回调。
    function resolve(value) {
        if (self.status === 'pending') {
            self.status = 'resolved';
            self.value = value;
        }

    }
    function reject(reason) {
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
        }
    }
    excutor(resolve, reject);
}

Primsie.prototype.then = function(onFulfilled, onRejected) {
    var self = this;
    if (self.status === 'resolved') {
        onFulfilled(self.value);
    }

    if (self.status === 'rejected') {
        onRejected(self.reason);
    }
    if (self.status === 'pending') {
        self.onResolvedCallbacks.push(function () {
            onFulfilled(self.value);
        });
        self.onRejectedCallbacks.push(function() {
            onRejected(self.reason);
        });
    }
}

成功或者失败的时候,执行onFulfilled和onRejected的函数。

function resolve(value) {
    if (self.status === 'pending') {
        self.status = 'resolved';
        self.value = value;
        self.onResolvedCallbacks.forEach(function (fn) {
            fn();
        })
    }
}

function reject(reason) {
    if (self.status === 'pending') {
        self.status = 'rejected';
        self.reason = reason;
        self.onRejectedCallbacks.forEach(function (fn) {
            fn();
        })
    }
}

2. 链式调用

Promise的链式调用和其它对象比如jquery的链式调用有所不同,Promise的then方法返回的是一个全新的Promise,而不是当前的Promise。因为Promise的状态只能改变一次,如果使用同一个Promise的话后面的then就失去了成功失败的自由性。

在then方法之后再去return一个新的Promise,原本的逻辑放在新创建的Promise内部即可,因为他是立即执行的一个函数。这里定义一个promise2接收新创建的Promise,在函数底部返回出去。还需要拿到then方法执行的结果,前一个then方法的返回值会传递给下一个then。如果x是一个普通值可以直接调用promise2的resolve方法,将这个值传递出去,这样下一个then就可以获取的到,所以执行resolve(x)。如果失败需要执行reject方法,这里使用try...catch捕获错误。

Primsie.prototype.then = function(onFulfilled, onRejected) {
    var self = this;
    const promise2 = new Promise(function (resolve, reject) {
        if (self.status === 'resolved') {
            try {
                const x = onFulfilled(self.value);
                resolve(x);
            } catch(e) {
                reject(e);
            } 
        }

        if (self.status === 'rejected') {
            try {
                const x = onRejected(self.reason);
                resolve(x);
            } catch(e) {
                reject(e);
            } 
        }
        if (self.status === 'pending') {
            self.onResolvedCallbacks.push(function () {
                try {
                    const x = onFulfilled(self.value);
                    resolve(x);
                } catch(e) {
                    reject(e);
                } 
            });
            self.onRejectedCallbacks.push(function() {
                try {
                    const x = onRejected(self.reason);
                    resolve(x);
                } catch(e) {
                    reject(e);
                } 
            });
        }
    })
    return promise2;
}

然后onFulfilled(self.value)返回的值不一定是一个常量,还可能是个promise,需要写一个方法来判断,如果返回值是promise就调用promise,否则才继续向resolve传递。

这里定义一个resolvePromise方法,在函数中判断返回值x和promse2的关系以及后续的处理,所以需要传递promise2参数,x参数,resolve参数和reject参数。

这4个参数是不能直接传递至resolvePromise中的,文档中要求他们不能在当前的上下文,所以要在try...catch代码块外层添加setTimeout在异步线程中添加。

function resolvePromise (promise2, x, resolve, reject) {

}

Primsie.prototype.then = function(onFulfilled, onRejected) {
    var self = this;
    const promise2 = new Promise(function (resolve, reject) {
        if (self.status === 'resolved') {
            setTimeout(function() {
                try {
                    const x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch(e) {
                    reject(e);
                } 
            }, 0)
        }

        if (self.status === 'rejected') {
            setTimeout(function() {
                try {
                    const x = onRejected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch(e) {
                    reject(e);
                }
            }, 0)
        }
        if (self.status === 'pending') {
            self.onResolvedCallbacks.push(function () {
                setTimeout(function() {
                    try {
                        const x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                }, 0)
            });
            self.onRejectedCallbacks.push(function() {
                setTimeout(function() {
                    try {
                        const x = onRejected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                }, 0)
            });
        }
    })
    return promise2;
}

3. resolvePromise函数

resolvePromise函数的作用是判断x是否是promise,如果是promise就执行并且将执行结果添加到resolve方法中,如果是常量则直接添加到resolve方法中。这些内容在文档上都可以找得到,具体可以自行翻阅文档,这里就不列出了,直接代码实现。

首先判断promise2和x引用了一个相同的对象,也就是他们是同一个promise对象。比如下面这种情况。

const p = new Promise(function(resolve, reject) {
    resolve('成功');
})
const promise2 = p.then(data => { // 这个时候x和promise2就是相等的,也就是自己等待自己去做做完什么事,等 和 做某事不能同时执行。
    return promise2;
})

应该抛出一个类型错误作为错误原因。

function resolvePromise (promise2, x, resolve, reject) {
    if (promise2 === x) { // 防止自己等待自己
        return reject(new TypeError('循环引用了'));
    }
}

如果x是promise类型,直接使用x的状态,也就是x成功就成功,x失败就失败。如果x是对象或者函数,就取他的then方法,获取then方法的时候如果出现异常,就执行失败。因为then方法可能是对象的一个不可访问的方法,get的时候报异常,所以我们需要使用try...catch去获取。

如果x不是promise类型,是个普通值,直接调用resolve就可以。

function resolvePromise (promise2, x, resolve, reject) {
    if (promise2 === x) { // 防止自己等待自己
        return reject(new TypeError('循环引用了'));
    }
    // x是object或者是个function
    if ((x !== null && typeof x === 'object') || typeof x === 'function') {
        try {
            let then = x.then;
        } catch (e) {
            reject(e);
        }
    } else {
        resolve(x);
    }
}

接着判断then,如果then是个函数,就认为他是Promise, 需要通过call执行then方法,改变this的指向为x,then中传入成功和失败的函数,官方文档中指明成功函数的参数叫y,失败的参数为r。

如果then不是一个函数那么当前这个then是一个普通对象,调用resolve方法直接返回即可。

try {
    let then = x.then;
    if (typeof then === 'function') {
        then.call(x, function (y) {
            resolve(y); // 成功的结果,让promise2变为成功状态
        }, function (r) {
            reject(r);
        });
    } else {
        resolve(x)
    }
} catch (e) {
    reject(e);
}

y有可能也是一个Promise,所以不能直接写resolve(y),应该递归判断y和promise2的关系。因为then返回的可能是Promise嵌套,也就是Promise中仍旧包含Promise,在Promise的标准中这样的写法是被允许的。所以要用递归来解决,拿到最终的返回,也就是基本类型。需要调用resolvePromise。y是then的成功回调返回的值,和之前的x基本一个概念。

try {
    let then = x.then;
    if (typeof then === 'function') {
        then.call(x, function (y) {
            resolvePromise (promise2, y, resolve, reject)
            // resolve(y); // 成功的结果,让promise2变为成功状态
        }, function (r) {
            reject(r);
        });
    } else {
        resolve(x)
    }
} catch (e) {
    reject(e);
}

自己编写的Promise可能会和别人的Promise嵌套使用,官方文档要求,Promise中要书写判断避免因对方Promise编写不规范带来的影响。比如对方的Promise成功和失败都调用了,或者多次调用了成功。需要使用called变量来表示Promise有没有被调用过,一旦状态改变就不能再改变了。

function resolvePromise (promise2, x, resolve, reject) {
    if (promise2 === x) { // 防止自己等待自己
        return reject(new TypeError('循环引用了'));
    }
    let called; // 表示Promise有没有被调用过
    // x是object或者是个function
    if ((x !== null && typeof x === 'object') || typeof x === 'function') {
        try {
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, function (y) {
                    if (called) { // 是否调用过
                        return;
                    }
                    called = true;
                    resolvePromise (promise2, y, resolve, reject)
                }, function (r) {
                    if (called) { // 是否调用过
                        return;
                    }
                    called = true;
                    reject(r);
                });
            } else { // 当前then是一个普通对象。
                resolve(x)
            }
        } catch (e) {
            if (called) { // 是否调用过
                return;
            }
            called = true;
            reject(e);
        }
    } else {
        if (called) { // 是否调用过
            return;
        }
        called = true;
        resolve(x);
    }
}

当前Promise还存在一个小问题,如果Promise有多个then方法,只在最后一个then方法中传递了onFulfilled,是需要将Promise的返回值传递过去的,也就是下面的代码需要用内容输出,这叫值的穿透。

p.then().then().then(function(data) {
    console.log(data);
})

实现起来也比较简单,假如用户没有传递onFulfilled,或者传入的不是函数,可以给个默认值,也就是这个参数是一个可选参数。

Primsie.prototype.then = function(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (data) { return data;};
    onRejected = typeof onRejected === 'function' ? onRejected : function (err) { throw err;};
}

最后在调用executor的时候也可能会出错,只要Promise出现错误,就需要走到then的reject中,所以这里也需要try...catch。

try {
    executor(resolve, reject);
} catch (e) {
    reject(e);
}

至此Promise就写完了,全部代码如下:

function Promise (executor) {
    var self = this;
    self.status = 'pending';
    self.value;
    self.reason;
    self.onResolvedCallbacks = []; // 存放所有成功的回调。
    self.onRejectedCallbacks = []; // 存放所有失败的回调。
    function resolve(value) {
        if (self.status === 'pending') {
            self.status = 'resolved';
            self.value = value;
            self.onResolvedCallbacks.forEach(function (fn) {
                fn();
            })
        }
    }

    function reject(reason) {
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
            self.onRejectedCallbacks.forEach(function (fn) {
                fn();
            })
        }
    }
    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

function resolvePromise (promise2, x, resolve, reject) {
    if (promise2 === x) { // 防止自己等待自己
        return reject(new TypeError('循环引用了'));
    }
    let called; // 表示Promise有没有被调用过
    // x是object或者是个function
    if ((x !== null && typeof x === 'object') || typeof x === 'function') {
        try {
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, function (y) {
                    if (called) { // 是否调用过
                        return;
                    }
                    called = true;
                    resolvePromise (promise2, y, resolve, reject)
                }, function (r) {
                    if (called) { // 是否调用过
                        return;
                    }
                    called = true;
                    reject(r);
                });
            } else { // 当前then是一个普通对象。
                resolve(x)
            }
        } catch (e) {
            if (called) { // 是否调用过
                return;
            }
            called = true;
            reject(e);
        }
    } else {
        if (called) { // 是否调用过
            return;
        }
        called = true;
        resolve(x);
    }
}

Promise.prototype.then = function(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (data) { return data;};
    onRejected = typeof onRejected === 'function' ? onRejected : function (err) { throw err;};
    var self = this;
    const promise2 = new Promise(function (resolve, reject) {
        if (self.status === 'resolved') {
            setTimeout(function() {
                try {
                    const x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch(e) {
                    reject(e);
                } 
            }, 0)
        }

        if (self.status === 'rejected') {
            setTimeout(function() {
                try {
                    const x = onRejected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch(e) {
                    reject(e);
                }
            }, 0)
        }
        if (self.status === 'pending') {
            self.onResolvedCallbacks.push(function () {
                setTimeout(function() {
                    try {
                        const x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                }, 0)
            });
            self.onRejectedCallbacks.push(function() {
                setTimeout(function() {
                    try {
                        const x = onRejected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                }, 0)
            });
        }
    })
    return promise2;
}

// 捕获失败的Promise,返回新的Promise
Promise.prototype.catch = function(reject) {
    return this.then(null, reject);
}

// 无论成功还是失败都会执行,并且最后返回原状态,返回新的Promise
Promise.prototype.finally = function(handle) {
    return this.then(function(value) {
        return Promise.resolve(handle()).then(function() {
            return value;
        })
    }, function (reason) {
        return Promise.resolve(handle()).then(function() {
            throw reason;
        })
    })
}

4. 测试

可以使用promises-aplus-tests测试Promise是否符合规范。测试的时候需要提供一段脚本,通过入口进行测试。

Promise.defer = Promise.deferred =  function() {
    let dfd = {};
    dft.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}
# 安装
npm install promises-aplus-tests -g

# 执行测试脚本
promises-aplus-tests promise.js

5. 静态方法实现

// 只要有一个失败就返回,否则返回所有Promise的结果list
Promise.all = function (values) {
    return new Promise(function (resolve, reject) {
        values = Array.isArray(values) ? values : []; 
        if (values.length === 0) {
            resolve(values);
        }
        var arr = []; // 最终结果的数组
        var index = 0;
        function processData (key, value) {
            index++;
            arr[key] = value;
            if (index === values.length) {
                resolve(arr);
            }
        }

        for (var i = 0; i < values.length; i++) {
            var current = values[i];
            if (current && current.then && typeof current.then === 'function') {
                current.then(function(y) {
                    processData(i, y);
                }, reject);
            } else {
                processData(i, current);
            }
        }
    });
}

// 只要有一个完成就返回
Promise.race = function (values) {
    return new Promise(function (resolve, reject) {
        for (var i = 0; i < values.length; i++) {
            var current = values[i];
            if (current && current.then && typeof current.then === 'function') {
                current.then(resolve, reject);
            } else {
                resolve(current);
            }
        }
    });
}

// 返回一个成功的Promise,如果入参是Promise直接返回
Promise.resolve = function(value){
    if (value && value.then && typeof value === 'function') {
        return value;
    }
    return new Promise((resolve,reject)=>{
        resolve(value);
    });
}

// 返回一个失败的Promise,如果入参是Promise,直接返回
Promise.reject = function(reason){
    if (reason && reason.then && typeof reason === 'function') {
        return value;
    }
    return new Promise((resolve,reject)=>{
        reject(reason);
    });
}

// 任意一个传入的Promise成功则成功,否则返回所有Promise结果的list。
Promise.any = function (values) {
    return new Promise(function(resolve, reject) {
        let errs = [];
        values.forEach((item, idx) => {
            if (item instanceof Promise) {
                item.then(function(value) {
                    resolve(value);
                }, function(err) {
                    errs[idx] = err;
                    if(errs.length === values.length) {
                        reject(errs);
                    }
                })
            } else {
                resolve(value);
            }
        })
    })
}

// 返回所有传入的promise对象结果值
Promise.allsettled = function(values) {
    return new Promise(function(resolve, reject) {
        var list = [];
        function ret (list) {
            if (list.length === values.length) {
                resolve(list);
            }
        }
        values.forEach((item, idx) => {
            if (item instanceof Promise) {
                item.then(function(value) {
                    list[idx] = {
                        status: 'fulfilled',
                        value,
                    }
                    ret(list);
                }, function(rea) => {
                    list[idx] = {
                        status: 'rejected',
                        reason: rea,
                    }
                    ret(list);
                })
            } else {
                list[idx] = {
                    status: 'fulfilled',
                    value: item,
                }
                ret(list);
            }
        })
    })
}
原文:https://zhiqianduan.com/article/JavaScript/Promise源码实现


链接: https://fly63.com/article/detial/11290

Js中的 forEach 源码

在日常 Coding 中,码农们肯定少不了对数组的操作,其中很常用的一个操作就是对数组进行遍历,查看数组中的元素,然后一顿操作猛如虎。今天暂且简单地说说在 JavaScript 中 forEach。

微信小程序代码源码案例大全

克隆项目代码到本地(git应该都要会哈,现在源码几乎都会放github上,会git才方便,不会的可以自学一下哦,不会的也没关系,gitHub上也提供直接下载的链接);打开微信开发者工具;

Node 集群源码初探

随着这些模块逐渐完善, Nodejs 在服务端的使用场景也越来越丰富,如果你仅仅是因为JS 这个后缀而注意到它的话, 那么我希望你能暂停脚步,好好了解一下这门年轻的语言,相信它会给你带来惊喜

Vue源码之实例方法

在 Vue 内部,有一段这样的代码:上面5个函数的作用是在Vue的原型上面挂载方法。initMixin 函数;可以看到在 initMixin 方法中,实现了一系列的初始化操作,包括生命周期流程以及响应式系统流程的启动

vue源码解析:nextTick

nextTick的使用:vue中dom的更像并不是实时的,当数据改变后,vue会把渲染watcher添加到异步队列,异步执行,同步代码执行完成后再统一修改dom,我们看下面的代码。

React源码解析之ReactDOM.render()

React更新的方式有三种:(1)ReactDOM.render() || hydrate(ReactDOMServer渲染)(2)setState(3)forceUpdate;接下来,我们就来看下ReactDOM.render()源码

React源码解析之ExpirationTime

在React中,为防止某个update因为优先级的原因一直被打断而未能执行。React会设置一个ExpirationTime,当时间到了ExpirationTime的时候,如果某个update还未执行的话,React将会强制执行该update,这就是ExpirationTime的作用。

扒开V8引擎的源码,我找到了你们想要的前端算法

算法对于前端工程师来说总有一层神秘色彩,这篇文章通过解读V8源码,带你探索 Array.prototype.sort 函数下的算法实现。来,先把你用过的和听说过的排序算法都列出来:

jQuery源码之extend的实现

extend是jQuery中一个比较核心的代码,如果有查看jQuery的源码的话,就会发现jQuery在多处调用了extend方法。作用:对任意对象进行扩;’扩展某个实例对象

vuex源码:state及strict属性

state也就是vuex里的值,也即是整个vuex的状态,而strict和state的设置有关,如果设置strict为true,那么不能直接修改state里的值,只能通过mutation来设置

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!