简述Promise背后的实现原理

在web开发中,我们往往离不开异步操作,过去我们的做法往往是通过回调函数多层嵌套来解决后一个异步操作依赖前一个异步操作,这样一层一层回调的嵌套很容易让人晕掉,更别提可维护性,为了解决回调的痛点,陆陆续续出现了一些解决方案比如事件发布订阅模式、事件的监听,再后来出现了Promise、Generator、async/await等等。co模块使用了Promise来自动执行Generator,async/await这个Node7.6开始默认支持的最新解决方案也依赖于Promise,所以了解Promise是非常有必要的,理解它背后的实现原理能让我们在使用它的时候更加游刃有余。

我们都知道 Promise 是通过.then方法来实现多个异步操作的顺序执行的,那么我们不免会疑惑,后绪的操作是如何得知前面异步操作什么时候完成呢?我们可能会想后面有一个函数在一直监听着前面异步操作的完成,这有点发布订阅模式模式的味道,不过因为.then 方法的链式调用,他又没有使用明显的发布订阅模式的方法(on、emit),这不免让实现变得看起来有点小复杂。

不过我们可以先从发布订阅模式着手思考,对于该模式首先会有一个事件数组用来收集事件,然后通过on将事件放入数组实现订阅,emit触发数组中相应事件,理解了这个以后,我们就可以开始去看Promise的实现了。

其实Promise的内部也有一个 defers 队列存放事件,而 .then 方法的作用和发布订阅模式的on方法一样是用来订阅事件的,每次调用 .then 方法就会往defers队列中放入一个事件,当异步操作完成时, resolve方法标示前一个异步过程完成并从defers队列中取出第一个事件执行并返回当前对象保证链式调用,以此类推,就完成了所有异步过程的队列执行。

示例代码:

function P(fn){
    var events = [];
    this.then = function(f){
        events.push(f);
        return this;
    }
    function resolve(value){
        var f = events.shift();
        f(value, resolve);
    }
    fn(resolve);
}

function a(){
    return new P(function(resolve){
        console.log("prepare getting a.....");
        setTimeout(function(){
            console.log("a getted!");
            resolve(1);
        }, 1000)
    });
}
a().then(function(value, resolve){
    console.log("preparing to get b...");
    setTimeout(function(){
        console.log("b getted!");
        resolve("data from b");
    }, 1000)
}).then(function(value, resolve){
    console.log(value)
});

运行结果:

prepare getting a.....
a getted!
preparing to get b...
b getted!
data from b
  • 支付宝二维码 支付宝
  • 微信二维码 微信
相关文章