[翻译]简单的实现一个Promise

        英文原文为:https://www.promisejs.org/implementing/

1. 状态机

        因为 promise 对象是一个状态机,所以我们首先应该定义将要用到的状态。

var PENDING = 0;
var FULFILLED = 1;
VAR REJECTED = 2;

function Promise () {
    // 可以存储这些状态,PENDING、FULFILLED 或 REJECTED
    var state = PENDING;

    // 当为 FULFILLED 或 REJECTED 状态时,存储值或错误信息
    var value = null;

    // 存储被 .then 或 .done 方法调用的成功和失败的处理函数
    var handlers = [];
}

2. 转换

        接着,让我们来考虑一下两个可能发生的重要转换,fulfilling 和 rejecting:

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise () {
    // 可以存储这些状态,PENDING、FULFILLED 或 REJECTED
    var state = PENDING;

    // 当为 FULFILLED 或 REJECTED 状态时,存储值或错误信息
    var value = null;

    // 存储被 .then 或 .done 方法调用的成功和失败的处理函数
    var handlers = [];

    function fulfill (result) {
        state = FULFILLED;
        value = result;
    }

    function reject (error) {
        state = REJECTED;
        value = error;
    }
}

        这给了我们基础的低层转换,我们现在来考虑更高层的转换 resolve。

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise () {
    // 可以存储这些状态,PENDING、FULFILLED 或 REJECTED
    var state = PENDING;

    // 当为 FULFILLED 或 REJECTED 状态时,存储值或错误信息
    var value = null;

    // 存储被 .then 或 .done 方法调用的成功和失败的处理函数
    var handlers = [];

    function fulfill (result) {
        state = FULFILLED;
        value = result;
    }

    function reject (error) {
        state = REJECTED;
        value = error;
    }

    function resolve (result) {
        try {
            var then = getThen(result);
            if (then) {
                doResolve(then.bind(result), resolve, reject);
                return;
            }
            fulfill(result);
        } catch (e) {
            reject(e);
        }
    }
}

        注意 resolve 如何判断接收的是一个 promise 对象还是一个基本值(plain value),如果是一个 promise 对象,如何等待他完成。

/** 
 * 检查值是否是一个 Promise 对象, 如果他是,
 * 返回这个 Promise 对象的‘then’方法。
 *
 * @param {Promise|Any} value
 * @return {Function|Null}
*/
function getThen (value) {
    var t = typeof value;
    if (value && (t === 'object' || t === 'function')) {
        var then = value.then
        if (typeof then === 'function') {
            return then;
        }
    }
    return null;
}

/**
 * 潜在的解析函数,以确保 onFulfilled 和 onRejected 只执行其中的一个
 */
function doResolve (fn, onFulfilled, onRejected) {
    var done = false;
    try {
        fn (function (value) {
            if (done) return
            done = true
            onFulfilled(value);
        }, function (reason) {
            if (done) return
            done = true
            onRejected(reason);
        })
    } catch (ex) {
        if (done) return
        done = true
        onRejected(ex);
    }
}

3. 构造

        我们现在有了完整的内部状态机,但我们还没有暴露出解决承诺(resolving promise)或观察它的方法。让我们从增加一个解决 promise 的方法开始。

var PENDING = 0;
var FULFILLED = 1;
VAR REJECTED = 2;

function Promise () {
    // 可以存储这些状态,PENDING、FULFILLED 或 REJECTED
    var state = PENDING;

    // 当为 FULFILLED 或 REJECTED 状态时,存储值或错误信息
    var value = null;

    // 存储被 .then 或 .done 方法调用的成功和失败的处理函数
    var handlers = [];

    function fulfill (result) {
        state = FULFILLED;
        value = result;
    }

    function reject (error) {
        state = REJECTED;
        value = error;
    }

    function resolve (result) {
        try {
            var then = getThen(result);
            if (then) {
                doResolve(then.bind(result), resolve, reject);
                return;
            }
            fulfill(result);
        } catch (e) {
            reject(e);
        }
    }

    doResolve(fn, resolve, reject);
}

        你可以看到,我们复用了 doResolve,因为我们有另一个不可信的解析。fn 允许多次调用 resolve 和 reject,甚至抛出异常。我们要确保 promise 只 resolve 一次或 reject 一次,然后再也不会过渡到另一个状态。

4. 观察(使用 .done)

        我们现在拥有一个已完成的状态机,但是我们仍没有办法去观察它的变化。我们的最终目的是实现 .then,但是 .done 的语义要简单的多,所以我们首先实现它。

        我们这里的目标是实现 promise.done(onFulfilled, onRejected):

  • 只调用 onFulfilled 或 onRejected 中的一个
  • 它是调用一次
  • 它在下一时刻被调用(例如,在 .done 方法已经返回后)
  • 不管我们在调用之前或之后是否解决了这个问题,它都被调用了。
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise (fn) {
    // 可以存储这些状态,PENDING、FULFILLED 或 REJECTED
    var state = PENDING;

    // 当为 FULFILLED 或 REJECTED 状态时,存储值或错误信息
    var value = null;

    // 存储被 .then 或 .done 方法调用的成功和失败的处理函数
    var handlers = [];

    function fulfill(result) {
        state = FULFILLED;
        value = result;
        handlers.forEach(handle);
        handlers = null;
    }

    function reject (result) {
        state = REJECTED
        value = result
        handlers.forEach(handle)
        handlers = null
    }

    function resolve (result) {
        try {
            var then = getThen(result)
            if (then) {
                doResolve(then, resolve, reject)
                return
            }
            fulfill(result)
        } catch (ex) {
            reject(ex)
        }
    }

    function handle (handler) {
        if (state === PENDING) {
            handlers.push(handler);
        } else {
            if (state === FULFILLED && typeof handlers.onFulfilled === 'function') {
                handler.onFulfilled(value);
            }
            if (state === REJECTED && typeof handlers.onRejected === 'function') {
                handler.onRejected(value);
            }
        }
    }

    this.done = function (onFulfilled, onRejected) {
        // 确保我们总是异步执行的
        setTimeout(function () {
            handler({
                onFulfilled: onFulfilled,
                onRejected: onRejected
            })
        }, 0)
    }
    
    doResolve(fn, resolve, reject);
}

        当 Promise 被解决或拒绝时,我们要确保去通知 handlers。

5. 观察(使用 .then)

        现在我们已经实现了 .done,我们可以简单的实现 .then 来实现相同的事情,但是在程序中构造一个新的 Promise 对象。

this.then = function (onFulfilled, onRejected) {
    var self = this;
    return new Promise(function (resolve, reject) {
        return self.done(function (result) {
            if (typeof onFulfilled === 'function') {
                try {
                    return resolve(onFulfilled(result));
                } catch (ex) {
                    return reject(ex);
                }
            } else {
                return resolve(result);
            }
        }, function (error) {
            if (typeof onRejected === 'function') {
                try {
                    return resolve(onRejected(error));
                } catch (ex) {
                    return reject(ex);
                }
            } else {
                return reject(error);
            }
        });
    });
}
posted @ 2018-03-15 14:45  水果味的C  阅读(648)  评论(0编辑  收藏  举报