Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promise #3

Open
zxw018018 opened this issue Aug 26, 2018 · 0 comments
Open

Promise #3

zxw018018 opened this issue Aug 26, 2018 · 0 comments

Comments

@zxw018018
Copy link
Owner

zxw018018 commented Aug 26, 2018

Promise

动机

JavaScript生来是一个单线程的语言,当我们想要做异步操作时,就不可避免用到回调函数。
回调函数很容易变成回调地狱,比如:

doSomething(function(result){
    doSomethingElse(result, function(newResult){
        doThirdThing(newResult, function(finalResult){
            console.log('Got the final result: ' + finalResult);
        }, failureCallback);
    }, failureCallback);
}, failureCallback);

这样的写法既复杂又不能很好的handle error。
于是在ES6中就引入了Promise这个概念,利用Promise,上面的代码就可以写成这样:

doSomething().then(function(result){
    return doSomethingElse(result);
})
.then(function(newResult){
    return doThirdThing(newResult);
})
.then(function(finalResult){
    console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

利用箭头函数arrow function,代码还能进一步简化:

doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
    console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);

状态机

Promise背后的核心思想是一个Promise代表一个异步操作的结果。
本质上,Promise就是一个状态机state machine。它有三种状态:

  1. pending 待定 —— 一个Promise最初的状态
  2. fulfilled 已实现 —— 表示一个成功操作的Promise的状态
  3. rejected 已拒绝 —— 表示一个失败操作的Promise的状态

下图就是这个状态机的状态转换图:
state_machine
下面我们就可以开始实现Promise了:

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
function Promise() { 
    // 保存状态
    var state = PENDING;

    //当已实现时保存值;当已拒绝时保存错误 
    var value = null;
    
    //当调用.then或.done时保存成功或失败句柄
    var handlers = [];
}

状态转换

我们先来实现两个关键的状态转换:实现fulfill和拒绝reject

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
function Promise() {
    var state = PENDING;
    var value = null;
    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() {
    var state = PENDING;
    var value = null;
    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和一个value的。如果接收的是Promise,就要等待它完成。
在代码中我们用到了几个辅助函数,我们来定义一下:

/**
* 检查value是不是一个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;
  }
  /**
  * 取一个可能有问题的resolver函数,保证onFulfilled和onRejected只被调用一次 *
  * 不保证异步 *
  * @param {Function} fn 一个不一定能被信任的resolver函数 
  * @param {Function} onFulfilled
  * @param {Function} 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)
  } 
}

构造

至此,我们已经完成了这个状态机,但我们还没有实现解决Promise和观察Promise的函数。
我们先来加个解决Promise的方法:

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
function Promise() {
    var state = PENDING;
    var value = null;
    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); 
        }
    } 

    deResolve(fn, resolve, reject);
}

我们保证Promise只被resolve或reject一次,然后再也不会再转换到不同的状态了。

观察(通过.done

我们现在有了一个完整的状态机,但我们还是没有任何方法可以去观察它的变化。
我们的最终目标是要实现.then,但.done更容易实现,我们先来实现它。

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
function Promise(fn) {
    var state = PENDING;
    var value = null;
    var handlers = [];

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

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

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

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

    this.done = function (onFulfilled, onRejected) { 
        //保证我们一直异步
        setTimeout(function () {
            handle({
                onFulfilled: onFulfilled,
                onRejected: onRejected
            }); 
        }, 0);
    }

    doResolve(fn, resolve, reject);
}

观察(通过.then

现在我们已经有了.done,我们就可以很容易的实现.then,我们在这个过程中要构造一个新的Promise,因为.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);
            }
        }); 
    });
}

有了Promise.then,其他像Promise.resolve,Promise.reject,Promise.race, Promise.all就很好实现了。

Promise.resolve(value)

返回一个以给定值解析后的promise对象。如果传入的value本身就是promise对象,则该对象作为Promise.resolve方法的返回值返回;否则以该值为成功状态返回promise对象。

实现

Promise.resolve = function (value) {
  return new Promise(function (resolve) {
    resolve(value);
  });
};

例子

var promise1 = Promise.resolve([1, 2, 3]);

promise1.then(function(value){
    console.log(value);
    // expected output: Array [1, 2, 3]
});

Promise.reject(reason)

返回一个带有拒绝原因reason参数的promise对象。

实现

Promise.reject = function (value) {
  return new Promise(function (resolve, reject) {
    reject(value);
  });
};

例子

Promise.reject(new Error("fail")).then(function(result){
    // 未被调用
}, function(error){
    console.log(error); // stacktrace
});

Promise.race(iterable)

返回一个promise,一旦迭代器中的某个promise解决或拒绝,返回的promise就会解决或拒绝。

实现

Promise.race = function (values) {
    // 这个实现只支持array-likes
    return new Promise(function (resolve, reject) {
        values.forEach(function(value){
            Promise.resolve(value).then(resolve, reject);
        }); 
    });
};

例子

var p1 = new Promise(function(resolve, reject){
    setTimeout(resolve, 500, "one");
});
var p2 = new Promise(function(resolve, reject){
    setTimeout(resolve, 100, "two");
});

Promise.race([p1, p2]).then(function(value){
    console.log(value); // "two"
    // Both resolve, but p2 is faster
});

var p3 = new Promise(function(resolve, reject){
    setTimeout(resolve, 500, "three");
});
var p4 = new Promise(function(resolve, reject){
    setTimeout(reject, 100, "four");
});

Promise.race([p3, p4]).then(function(value){
    // not called
}, function(reason){
    console.log(reason); // "four"
    // p4 is faster, so it rejects
});

Promise.all(iterable)

返回一个Promise实例,此实例在iterable参数内所有的promise都resolved或参数中不包含promise时 resolve;如果参数中promise有一个rejected,此实例reject,失败原因是第一个失败promise的结果。

实现

Promise.all = function (arr) {
    // 这个实现只支持array-likes
    var args = Array.prototype.slice.call(arr);
    return new Promise(function (resolve, reject) {
        if (args.length === 0) return resolve([]);
        var remaining = args.length;
        function res(i, val) {
            if (val && (typeof val === 'object' || typeof val === 'function')) {
                var then = val.then;
                if (typeof then === 'function') {
                    var p = new Promise(then.bind(val));
                    p.then(function (val) {
                        res(i, val);
                    }, reject);
                    return;
                } 
            }
            args[i] = val;
            if (--remaining === 0) {
                resolve(args);
            }
        }
        for (var i = 0; i < args.length; i++) {
            res(i, args[i]);
        }
    }); 
};

例子

var promise = Promise.resolve(3);
Promise.all([true, promise]).then(values => {
    console.log(values); // [true, 3]
});

参考资料

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant