Quantcast
Channel: CNode:Node.js专业中文社区
Viewing all articles
Browse latest Browse all 14821

简单实现 Promise A+

$
0
0

本文代码实现 Promise/A+ 规范

首先

首先当然要定义一些 Promise 要用到的状态变量和垫片函数:

// 定义 Promise 状态
const PENDING = 0;
const REJECTED = -1;
const FULFILLED = 1;

// 用于创建空的 Promise 对象
const NOOP = function () {};

// 2.2.4 notes#3.1
// 用于异步调用 onFulfilled & onRejected
var asyncCall = (process && process.nextTick) ||
    setImmediate ||
    function (callback) {
        if (typeof callback !== 'function')
            throw new TypeError('callback is not a function');
        setTimeout(callback, 0);
    };

接下来就是标准式的构造函数了:

function Promise (excutor) {
    if (!(this instanceof Promise)) {
        return new Promise(excutor);
    }
    
    if (typeof excutor !== 'function') {
    	  throw new TypeError('Promise executor is not a function');
    }
    
    // 初始化 pending 状态
    this._state = PENDING;
    
    // value & reason of Promise
    this._value = null;
    this._reason = null;
    
    // 在被 fulfill 或 reject 之前,
    // 保留对回调函数 onFulfilled 和 onRejected 的引用
    this._subscribers = [];
    
    var promise = this;
    try {
        excutor(function (value) {
            //todo: resolve `promise` with value
            __resolve__(promise, value);
        }, function (reason) {
            //todo: reject `promise` with reason
            __reject__(promise, reason);
        })
    } catch (exception) {
        //todo: reject `promise` with `exception` as reason
        __reject__(promise, exception);
    }
}

fulfill & reject

Promise 只可能由 pending 向 fulfilled 或 rejected 两种状态之一转化且不可逆转。

promise.jpg

分别定义函数 fulfill(promise, value)reject(promise, reason)来完成这一过程:

(为了避免和其他函数中的同名参数引起混淆,我就直接采用前后加两个下划线的方式命名了)

function __fulfill__(promise, value) {
    // 先做状态检查
    if (promise._state === PENDING) {
        // 改变 `promise` 的状态
        promise._state = FULFILLED;
        promise._value = value;
        
        // 依次调用已绑定的回调函数
        var subscribers = promise._subscribers;
        for (var i = 0; i < subscribers.length; ++i) {
            //todo: 此处应调用 onFulfilled
            invokeCallback(promise, subscibers[i]);
        }
    }
}

function __reject__(promise, reason) {
    // 状态检查
    if (promise._state === PENDING) {
        // 改变 `promise` 的状态
        promise._state = REJECTED;
        promise._reason = reason;
        
        // 依次调用已绑定回调函数
        var subscribers = promise._subscribers;
        for (var i = 0; i < subscribers.length; ++i) {
            //todo: 此处应调用 onRejected
            invokeCallback(promise, subscribers[i]);
        }
    }
}

Promise 的解析过程(Promise Resolution Procedure)

估计不少同学已经发现了,通常我们写 Promise 的时候通常会把 excutor函数的参数命名为 resolvereject而不是 fulfillreject。因为实际上 resolve 和 fulfill 并不是相同的过程,此处引用下规范中的原文:

The promise resolution procedure is an abstract operation taking as input a promise and a value, which we denote as [[Resolve]](promise, x). If x is a thenable, it attempts to make promise adopt the state of x, under the assumption that x behaves at least somewhat like a promise. Otherwise, it fulfills promise with the value x.

当使用一个值 x去 resolve 一个 promise 的时候,会先判断 x是否是一个 thenable (promise 对象和类 promise 对象),如果是则尝试使 promise 的状态与该 thenable保持一致;如果不是则直接用 x fulfill 这个 promise。

总结来说 resolve 一个 promise 。promise 可能变成 fulfilled 也可能变成 rejected。那么接下来按照规范,依葫芦画瓢地实现这一过程。

function __resolve__(promise, x) {
    // 2.3.1
    if (promise === x)
        return __reject__(promise,
            new TypeError('try to resolve a promise with itself'));

    // 2.3.2 如果 `x` 是一个 Promise 对象,
    // 使 `promise` 和 `x` 的状态保持一致
    if (x instanceof Promise) {
        if (x._state === PENDING) {
            x.then(function (v) {
                __resolve__(promise, v);
            }, function (r) {
                __reject__(promise, r);
            });
        } else if (x._state === FULFILLED) {
            __resolve__(promise, x._value);
        } else {
            __reject__(promise, x._reason);
        }
        return;
    }
    
    var type = typeof x;
    
    //!!! 注意此处 typeof null 结果会是 'object'
    if ((type === 'object' && x !== null) || type === 'function') {
        // 检查该对象或函数是否为 thenable
        handleThenable(promise, x);
    } else {
        // 2.3.4 其他类型的值直接 fulfill
        __fulfill__(promise, x);
    }
}

// 2.3.3
function handleThenable(promise, x) {
    var then = null; // 取值 `x.then`
    
    // 标记 resolvePromise 或 rejectPromise 其一是否被调用过
    //!!! 不可缺少,[[resolve]] 过程不能保证立即使 "promise" 的状态发生变化
    var isCalled = false; 
    
    try {
        then = x.then;
        
        if (typeof then === 'function') {
            try {
                then.call(x, function resolvePromise(v) {
                    if (isCalled) return;
                    
                    __resolve__(promise, v);
                    isCalled = true;
                }, function rejectPromise(r) {
                    if (isCalled) return;
                    
                    __reject__(promise, r);
                    isCalled = true;
                });
            } catch (e1) {
                if (!isCalled) {
                    __reject__(promise, e1);
                }
                // 如果 resolvePromise 或 rejectPromise
                // 已被调用,忽略该异常
            }
        }
        // 2.3.3.4 不是 thenable
        else {
            __fulfill__(promise, x);
        }
    } catch (e2) {
        // 2.3.3.2
        __reject__(promise, e2);
    }
}

then 方法的实现

规范规定,promise 的 then 方法可以被多次调用,每次调用都会返回一个 promise 对象。在回调函数被正式调用前我们需要保持对新的 promise 的引用,建立一个 { promise2, onFulfilled, onRejected } 三元组用于记录回调函数和 promise 对应关系。

// { promise2, onFulfilled, onRejected } 三元组
function Triad(promise, onFulfilled, onRejected) {
    if (!(this instanceof Triad)) {
        return new Triad(promise, onFulfilled, onRejected);
    }
    
    this._promise = promise;
    
    // 2.2.1 当 onFulfilled 和 onRejected 不是函数时将会被忽略
    // 2.2.5 onFulfilled 和 onRejected 必须以函数的形式调用
    this._onFulfilled = typeof onFulfilled === 'function'
        ? onFulfilled.bind(undefined)
        : null;
    this._onRejected = typeof onRejected === 'function'
        ? onRejected.bind(undefined) :
        : null;
}
Promise.prototype.then = function (onFulfilled, onRejected) {
    // then 方法必须返回一个新的 promise
    var promise2 = new Promise(NOOP);
    var triad = new Triad(promise2, onFulfilled, onRejected);
    
    if (this._state === PENDING) {
        // pending,直接将三元组加入订阅队列,保证顺序!
        this._subscribers.push(triad);
    } else {
        // 已经 fulfilled 或 rejected 就直接(异步)执行回调函数
        invokeCallback(this, triad);
    }
    
    return promise2;
}

invokeCallback

/* 根据 promise 的状态,执行三元组中的回调函数 */
function invokeCallback(promise, triad) {
    var state = promise._state;
    var value = promise._value;
    var reason = promise._reason;

    var promise2 = triad._promise;
    var onFulfilled = triad._onFulfilled;
    var onRejected = triad._onRejected;

    if (state === FULFILLED) {
        if (onFulfilled) {
            asyncCall(function() {
                try {
                    __resolve__(promise2, onFulfilled(value));
                } catch (e) {
                    __reject__(promise2, e);
                }
            });
        } else {
            __fulfill__(promise2, value);
        }
    } else if (state === REJECTED) {
        if (onRejected) {
            asyncCall(function () {
                try {
                    __resolve__(promise2, onRejected(reason));
                } catch (e) {
                    __reject__(promise2, e);
                }
            });
        } else {
            __reject__(promise2, reason);
        }
    }
}

Viewing all articles
Browse latest Browse all 14821

Trending Articles