深入实现Promise A+规范

Promise对目前的前端开发者来说已经是必须去了解甚至掌握的一个东西了,ES6规范中甚至定义了原生的Promise对象,对于它到底是什么的问题请参考这篇文章Promise基础

因为最初Promise的概念提出的时候,一群人为了规范它,产生了一个叫Promise A的规范,而本文要说的是Promise A+,没错多了一个加号,说明是有不一样的,详细的规范细节请点击Promise/A+,该规范是对 Promise/A 规范的补充和修改,它出现的目的是为了统一异步编程中的接口,本文会就结合规范来一步步实现Promise A+代码

Promise实现的几个重点

  • 要保证then方法回调的时序,即第一个promise兑现的时候,后面链式调用的then方法参数回调依次调用,但同时这些方法又不能在执行then方法的时候立即执行,必须等待前面promise兑现

第一个promise的兑现是在2秒后,2秒后我才能执行fn1函数,接着执行fn2函数,但是2秒内我的then方法就已经结束返回了。解决办法很简单,引入一个函数数组队列来保存then方法的参数fn1,fn2,2秒之后再来执行。

var promise = new Promise(function (resolve, reject) {  
        setTimeout(function(){
            resolve(100);
        },2000)

    })

    promise.then(function fn1(val){
        return val+100
    }).then(function fn2(val){//这里为了便于说明给匿名函数加了个名字
        alert(val);
    })
  • 如果then方法执行的时候前面的promise已经兑现了,那后面链式调用then方法的回调如fn3应该立即执行,那如何知道它已经兑现了?这也是为什么会定义promise的状态.
var promise = new Promise(function (resolve, reject) {  
        setTimeout(function(){
            resolve(100);
        },2000)

    })

    promise.then(function fn1(val){
        return val+100
    }).then(function fn2(val){//这里为了便于说明给匿名函数加了个名字
        alert(val);
    }).then(function fn3(val){
        alert(val);
    }
  • promise A+规范规定所有的then的2个参数的回调都应该是异步的,必须在then方法返回之后异步执行,对于我们实现来说就是必须把回调加入到下一个函数队列循环中,常用的有setTimeout,node中可以用process.nextTick,后面实现会说明
var promise = new Promise(function (resolve, reject) {  
        setTimeout(function(){
            resolve(100);
        },2000)

    })
    //这里fn1必须在then方法返回之后才能异步调用
    var promise1=promise.then(function fn1(val){
        return val+100
    })
  • 关于规范中promise2 = promise1.then(onFulfilled, onRejected);

仔细看规范知道,这里可能会有3个promisepromise1,onFulfilled返回的thenpromisepromise2,promise2必须在thenpromise兑现之后才能reslove

promise的几种状态

  • pending表示正在发生中
  • fulfilled表示已经履行
  • rejected表示被拒绝
var State = {  
    PENDING: 0,
    FULFILLED: 1,
    REJECTED: 2
};

then方法

then方法必须同样返回一个promise对象,实现如下,请对照Promise A+规范,注释中的数字会有标识

function then(onFulfilled, onRejected) {

        var self = this;

        var promise = new Promise(function () {
        });

        self._subscribers.push({//2.2.6
            fulfillPromise: typeof onFulfilled == "function" ? onFulfilled : null,//2.2.1,2.2.5
            rejectPromise: typeof onRejected == "function" ? onRejected : null,//2.2.1,2.2.5
            thenPromise: promise
        })

        invokeCallback(self);

        return promise; //2.2.7
    }

invokeCallback方法

用于处理promise兑现之后的回调队列

function invokeCallback(promise) {

    if (promise._state === State.PENDING) {

        return;

    }

    async(function () {//2.2.4

        while (promise._subscribers.length) {
            var obj = promise._subscribers.shift();//2.2.6
            try {
                //先把当前promise的值作为参数传递给fulfillPromise,然后后面根据返回的value值来作为兑现thenPromise的参数,这里是保证调用顺序的基础
                var value = (promise._state === State.FULFILLED ?
                    (obj.fulfillPromise || function (x) {
                        return x;//2.2.7.3
                    }) :
                    (obj.rejectPromise || function (x) {
                        throw x;//2.2.7.4
                    }))
                (promise._value);//2.2.2,2.2.3

            } catch (e) {

                reject(obj.thenPromise, e);//2.2.7.2

                continue;
            }

            resolve(obj.thenPromise, value);//2.2.7.1

        }

    })

}

//3.1 依赖于执行环境
function async(fn) {

    setTimeout(fn, 0);

}

[[Resolve]](promise, x)方法

resolve作为规范里面严格定义的一个过程,实现如下

function resolve(promise, x) {

    if (promise === x) {//2.3.1

        reject(promise, new TypeError("it cant't fulfill promise equals value condition"));

    }

    else if (x && x.constructor === Promise) {//2.3.2

        if (x._state === State.PENDING) {//2.3.2.1

            x.then(function (val) {

                resolve(promise, val)

            }, function (reason) {

                reject(promise, reason);

            })
        }
        else if (x._state === State.FULFILLED) {//2.3.2.2

            fulfill(promise, x._value);

        }
        else if (x._state === State.REJECTED) {//2.3.2.3

            reject(promise, x._value);

        }
    }
    else if (x !== null && (typeof x == "object" || typeof x == "function")) {

        var isCalled = false;//2.3.3.3.3

        try {

            var then = x.then;//2.3.3.1

            if (typeof then == "function") {//2.3.3.3

                then.call(x, function (val) {

                    isCalled || resolve(promise, val);//2.3.3.3.1

                    isCalled = true;

                }, function (reason) {

                    isCalled || reject(promise, reason);//2.3.3.3.2

                    isCalled = true;
                })
            }

            else {

                fulfill(promise, x);//2.3.3.4

            }

        } catch (e) {

            isCalled || reject(promise, e);//2.3.3.2 ,2.3.3.3.4

        }
    }
    else {
        fulfill(promise, x);//2.3.4
    }
}

Promise A+与Promise A的主要区别

  • A+规范强调了不同实现之间的互操作和混用,通过术语thenable来区分promise对象,当一个对象拥有then函数就认为是promise对象,使用鸭子类型判断来允许不同实现之前的兼容和互相操作

  • A+定义当onFulfilled或者onRejected返回promise时后的处理过程,他们必须是作为函数来调用,而且调用过程必须是异步的

  • A+严格定义了then方法链式调用时onFulfilled或者onRejected的调用顺序

插曲

为什么jquery不是标准的promise实现 该文的作者说明了为什么jquery promise不是一个符合规范的实现,主要观点是认为jquery的then方法返回的promise不是一个新的promise,而只是去修改当前promise的状态,但规范严格规定了状态之间的过渡是不可逆的

参考文章

Promise A+规范

JavaScript Promises There and back again

在Node.js 中用 Q 实现Promise – Callbacks之外的另一种选择