JS 异步发展流程 —— Promise

852 阅读11分钟

实现一个自己的Promise库

上一篇中我们介绍了整个js异步发展的过程,今天我们就来自己完成一个Promise库,可以参照Promise的实现规范

首先先搭一个最简单的Promise框架

function Promise(executor) { // executor是一个执行函数
    let self = this;
    self.status = 'pending';
    self.value = undefined; // 默认成功的值
    self.reason = undefined; // 默认失败的原因
    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;
        }
    }
    executor(resolve,reject)
}
Promise.prototype.then = function(onFulfilled,onRjected){
    let self = this;
    if(self.status === 'resolved'){
        onFulfilled(self.value);
    }
    if(self.status === 'rejected'){
        onRjected(self.reason);
    }
}

module.exports = Promise

这里可以看到我们给Promise对象定义了3种状态以及一个用来当做默认成功会传入的值value和失败会传入的原因reason,同时定义了resolve和reject方法,将它们传入executor,也就是我们在调用生成Promise对象时会传入的执行函数,里面放着我们需要调用的异步逻辑.

接着我们通过Promise的原型定义了一个then的公有方法,它会接受两个参数onFullfilled和onRejected,这两个参数其实就是两个函数,是我们用来定义成功或失败时所执行的逻辑.

但是这里的问题是当我们executor里执行的是异步逻辑时,如果我们调用then,其实status依旧是pending的状态也就根本不会调用到onFullfilled或onRejected,所以我们需要定义两个callback数组来存放我们在pending状态下调用过的onFullfilled和onRejected函数,同时,在resolve和reject函数中我们需要将对应的callback数组中的onFullfilled和onRejected依次取出调用.

所以让我们来看下改进之后的代码:

function Promise(executor) { // executor是一个执行函数
    let self = this;
    self.status = 'pending';
    self.value = undefined; // 默认成功的值
    self.reason = undefined; // 默认失败的原因
    self.onResolvedCallbacks = []; // 存放then成功的回调
    self.onRejectedCallbacks = []; // 存放then失败的回调
    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();
            })
        }
    }
    executor(resolve, reject)
}

Promise.prototype.then = function (onFulfilled, onRjected) {
    let self = this;
    if (self.status === 'resolved') {
            onFulfilled(self.value);
    }
    if (self.status === 'rejected') {
            onRjected(self.reason);
    }
    // 当调用then时可能没成功 也没失败
    if (self.status === 'pending') {
            // 此时没有resolve 也没有reject
            self.onResolvedCallbacks.push(function () {
                onFulfilled(self.value);
            });
            self.onRejectedCallbacks.push(function () {
                onRjected(self.reason);
            });
    }
}

下一步,我们就要开始考虑如何支持链式调用的情况了,如前文所说,链式调用要求我们每次调用then之后都需要返回一个新的Promise对象.

所以在我们需要对onFullfilled和onRejected返回的结果进行判断分析来决定之后then调用时是走向成功还是失败。返回的值有多种情况,它可能是一个值,也可能是另一个Promise对象。所以让我们定义一个新的函数用来解析.

function resolvePromise(p2,x,resolve,reject){
    // 有可能这里返回的x是别人的promise
    // 尽可能允许其他乱写
    if(p2===x){ //这里应该报一个类型错误,有问题
        return reject(new TypeError('循环引用了'))
    }
    // 看x是不是一个promise,promise应该是一个对象
     if(x!==null||(typeof x === 'object'||typeof x === 'function')){
        // 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
        try{ // {then:1}
            let then = x.then;
            if(typeof then === 'function'){
                // 成功
                then.call(x,function(y){
                    // y可能还是一个promise,在去解析直到返回的是一个普通值
                    resolvePromise(promise2,y,resolve,reject)
                },function(err){ //失败
                    reject(err);
                })
            }else{
                resolve(x)
            }
        }catch(e){
            reject(e);
        }
     }else{ // 说明是一个普通值1
        resolve(x); // 表示成功了
     }
}

这里判断的逻辑是先判断返回值x是不是指向当前将会返回的Promise对象自己,如果是自己的话就变成是自己等待自己,那我们就直接抛出一个循环引用的错误,然后接着判断x是不是一个对象,如果不是就说明是普通值,我们直接返回成功resolve并传入值x,如果是对象的话并且有一个then的方法的话我们就当他是一个Promise对象,如果不是Promise对象的话我们也就直接返回成功resolve并传入对象x,如果是的话那我们就直接调用then方法去判定它的成功与失败,失败的话我们就直接调用reject,成功的话我们还需在判断成功的值是否为Promise对象,如果不是的话我们可以直接resolve,是的话则我们要继续递归调用resolvePromise方法去解析一直到获取一个值位置.

我们看一下目前为止的代码:

function Promise(executor) { // executor是一个执行函数
    let self = this;
    self.status = 'pending';
    self.value = undefined; // 默认成功的值
    self.reason = undefined; // 默认失败的原因
    self.onResolvedCallbacks = []; // 存放then成功的回调
    self.onRejectedCallbacks = []; // 存放then失败的回调
    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(p2,x,resolve,reject){
    // 有可能这里返回的x是别人的promise
    // 尽可能允许其他乱写
    if(p2===x){ //这里应该报一个类型错误,有问题
        return reject(new TypeError('循环引用了'))
    }
    // 看x是不是一个promise,promise应该是一个对象
     if(x!==null||(typeof x === 'object'||typeof x === 'function')){
        // 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
        try{ // {then:1}
            let then = x.then;
            if(typeof then === 'function'){
                // 成功
                then.call(x,function(y){
                    // y可能还是一个promise,在去解析直到返回的是一个普通值
                    resolvePromise(promise2,y,resolve,reject)
                },function(err){ //失败
                    reject(err);
                })
            }else{
                resolve(x)
            }
        }catch(e){
            reject(e);
        }
     }else{ // 说明是一个普通值1
        resolve(x); // 表示成功了
     }
}

Promise.prototype.then = function (onFulfilled, onRjected) {
    let self = this;
    let promise2; //返回的promise
    if (self.status === 'resolved') {
        promise2 = new Promise(function (resolve, reject) {
            // 当成功或者失败执行时有异常那么返回的promise应该处于失败状态
            // x可能是一个promise 也有可能是一个普通的值
            let x = onFulfilled(self.value);
            // x可能是别人promise,写一个方法统一处理
            resolvePromise(promise2,x,resolve,reject);
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
             let x = onRjected(self.reason);
             resolvePromise(promise2,x,resolve,reject);
        })
    }
    // 当调用then时可能没成功 也没失败
    if (self.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {
            // 此时没有resolve 也没有reject
            self.onResolvedCallbacks.push(function () {
                let x = onFulfilled(self.value);
                resolvePromise(promise2,x,resolve,reject);
            });
            self.onRejectedCallbacks.push(function () {
                let x = onRjected(self.reason);
                resolvePromise(promise2,x,resolve,reject);
            });
        })
    }
    return promise2;
}

module.exports = Promise

下一步,由于规范中要求返回的Promise对象中的onFullfilled和onRejected执行逻辑必须遵从是异步的,所以我们可以用一个setTimeout函数来包住执行逻辑

setTimeout(function(){
    try{
        let x = onFulfilled(self.value);
        // x可能是别人promise,写一个方法统一处理
        resolvePromise(promise2, x, resolve, reject);
    }catch(e){
        reject(e);
    }
})

同时如果调用then时没有传入onFullfilled或onRejected的话我们可以定义一个默认的函数给它们,这样就可以解决到值穿透的问题,例如promise.then().then().then()这样的调用

onFulfilled = typeof onFulfilled === 'function'?onFulfilled:function(value){
    return value;
}
onRjected = typeof onRjected === 'function'?onRjected:function(err){
    throw err;
}

另外一个问题就是有些Promise的实现可能可以既调用成功又失败,那么对于这种情况我们就需要专门处理,永远只处理第一个调用的

    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        // 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
        try { // {then:1}
            let then = x.then;
            if (typeof then === 'function') {
                // 成功
                then.call(x, function (y) {
                    if (called) return
                    called = true
                    // y可能还是一个promise,在去解析直到返回的是一个普通值
                    resolvePromise(promise2, y, resolve, reject)
                }, function (err) { //失败
                    if (called) return
                    called = true
                    reject(err);
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true;
            reject(e);
        }
    } else { // 说明是一个普通值1
        resolve(x); // 表示成功了
    }

以上我们就完成了一个满足规范的Promise实现了,最后的实现代码如下:

function Promise(executor) { // executor是一个执行函数
    let self = this;
    self.status = 'pending';
    self.value = undefined; // 默认成功的值
    self.reason = undefined; // 默认失败的原因
    self.onResolvedCallbacks = []; // 存放then成功的回调
    self.onRejectedCallbacks = []; // 存放then失败的回调
    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) {
    // 有可能这里返回的x是别人的promise
    // 尽可能允许其他乱写
    if (promise2 === x) { //这里应该报一个类型错误,有问题
        return reject(new TypeError('循环引用了'))
    }
    // 看x是不是一个promise,promise应该是一个对象
    let called; // 表示是否调用过成功或者失败
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        // 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
        try { // {then:1}
            let then = x.then;
            if (typeof then === 'function') {
                // 成功
                then.call(x, function (y) {
                    if (called) return
                    called = true
                    // y可能还是一个promise,在去解析直到返回的是一个普通值
                    resolvePromise(promise2, y, resolve, reject)
                }, function (err) { //失败
                    if (called) return
                    called = true
                    reject(err);
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true;
            reject(e);
        }
    } else { // 说明是一个普通值1
        resolve(x); // 表示成功了
    }
}

Promise.prototype.then = function (onFulfilled, onRjected) {
    //成功和失败默认不穿给一个函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    }
    onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
        throw err;
    }
    let self = this;
    let promise2; //返回的promise
    if (self.status === 'resolved') {
        promise2 = new Promise(function (resolve, reject) {
            // 当成功或者失败执行时有异常那么返回的promise应该处于失败状态
            // x可能是一个promise 也有可能是一个普通的值
            setTimeout(function () {
                try {
                    let x = onFulfilled(self.value);
                    // x可能是别人promise,写一个方法统一处理
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                try {
                    let x = onRjected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })

        })
    }
    // 当调用then时可能没成功 也没失败
    if (self.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {
            // 此时没有resolve 也没有reject
            self.onResolvedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                })
            });
            self.onRejectedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onRjected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            });
        })
    }
    return promise2;
}

module.exports = Promise;

对于以上代码实现,我们可以通过一个promises-aplus-tests的库来验证是否达成规范要求

可以通过npm install -g promises-aplus-tests,使用时直接promises-aplus-tests 文件名即可

接下来让我们来实现一个类似于之前介绍的Q库中defer的语法糖,首先我们看一下以下这段代码:

function read() {
    return new Promise(function(resolve,reject) {
        require('fs').readFile('./2.promise/1.txt', 'utf8', function (err, data) {
            if(err) reject(err);
            resolve(data);
        })
    })
}
read().then(function (data) {
    console.log(data)
},function(err){
    console.log(err);
})

这里我们封装了一个read函数,并返回一个Promise对象,但是可以看到这里我们必须在异步逻辑外面包一层Promise.我们可以通过定义Promise.defer的这样一个语法糖来简化这一层代码.

Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}

let Promise = require('./Promise');
function read() {
    let defer = Promise.defer()
    require('fs').readFile('./2.promise/1.txt', 'utf8', function (err, data) {
        if(err) defer.reject(err);
        defer.resolve(data);
    })
    return defer.promise;
}
read().then(function (data) {
    console.log(data)
},function(err){
    console.log(err);
})

同理包括catch,all,race,resolve,reject我们都可以自己实现,这里就不多加赘述,直接上最终的代码:

function Promise(executor) { // executor是一个执行函数
    let self = this;
    self.status = 'pending';
    self.value = undefined; // 默认成功的值
    self.reason = undefined; // 默认失败的原因
    self.onResolvedCallbacks = []; // 存放then成功的回调
    self.onRejectedCallbacks = []; // 存放then失败的回调
    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) {
    // 有可能这里返回的x是别人的promise
    // 尽可能允许其他乱写
    if (promise2 === x) { //这里应该报一个类型错误,有问题
        return reject(new TypeError('循环引用了'))
    }
    // 看x是不是一个promise,promise应该是一个对象
    let called; // 表示是否调用过成功或者失败
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        // 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
        try { // {then:1}
            let then = x.then;
            if (typeof then === 'function') {
                // 成功
                then.call(x, function (y) {
                    if (called) return
                    called = true
                    // y可能还是一个promise,在去解析直到返回的是一个普通值
                    resolvePromise(promise2, y, resolve, reject)
                }, function (err) { //失败
                    if (called) return
                    called = true
                    reject(err);
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true;
            reject(e);
        }
    } else { // 说明是一个普通值1
        resolve(x); // 表示成功了
    }
}

Promise.prototype.then = function (onFulfilled, onRjected) {
    //成功和失败默认不穿给一个函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    }
    onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
        throw err;
    }
    let self = this;
    let promise2; //返回的promise
    if (self.status === 'resolved') {
        promise2 = new Promise(function (resolve, reject) {
            // 当成功或者失败执行时有异常那么返回的promise应该处于失败状态
            // x可能是一个promise 也有可能是一个普通的值
            setTimeout(function () {
                try {
                    let x = onFulfilled(self.value);
                    // x可能是别人promise,写一个方法统一处理
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                try {
                    let x = onRjected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })

        })
    }
    // 当调用then时可能没成功 也没失败
    if (self.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {
            // 此时没有resolve 也没有reject
            self.onResolvedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                })
            });
            self.onRejectedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onRjected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            });
        })
    }
    return promise2;
}

// 捕获错误的方法
Promise.prototype.catch = function (callback) {
    return this.then(null, callback)
}

// 解析全部方法
// let arr = [];
// arr[1] = 100;
// console.log(arr.length)
Promise.all = function (promises) {
    //promises是一个promise的数组
    return new Promise(function (resolve, reject) {
        let arr = []; //arr是最终返回值的结果
        let i = 0; // 表示成功了多少次
        function processData(index, y) {
            arr[index] = y;
            if (++i === promises.length) {
                resolve(arr);
            }
        }
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(function (y) {
                processData(i, y)
            }, reject)
        }
    })
}

// 只要有一个promise成功了 就算成功。如果第一个失败了就失败了
Promise.race = function (promises) {
    return new Promise(function (resolve, reject) {
        for (var i = 0; i < promises.length; i++) {
            promises[i].then(resolve,reject)
        }
    })
}

// 生成一个成功的promise
Promise.resolve = function(value){
    return new Promise(function(resolve,reject){
        resolve(value);
    })
}

// 生成一个失败的promise
Promise.reject = function(reason){
    return new Promise(function(resolve,reject){
        reject(reason);
    })
}

Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}

module.exports = Promise;