Promise深入原理

250 阅读2分钟

准备从最基础的promise慢慢讲上去

基础版

function Promise(fn) {
    var value = null,
        callbacks = [];  //callbacks为数组,因为可能同时有很多个回调

    this.then = function (onFulfilled) {
        callbacks.push(onFulfilled);
        //因为要链式调用then(),
        return this;
    };

    function resolve(value) {
        callbacks.forEach(function (callback) {
            callback(value);
        });
    }

    fn(resolve);
}

先分析上面的代码,new Promise()之后,fn(resolve)函数就先执行了,fn是一个耗时的异步操作。但是我想让fn执行完之后,再执行resolve函数,我该怎么办?我们可以看到有个then方法,then是向队列里面加函数的,resolve是循环执行对列里面的函数的。说到这里,大概就知道异步操作是怎么搞的了。就是先fn->通过then加回调函数到队列->最后执行resolve,把队列里面的函数拿出来去变遍历执行。

有个问题,假如resolve执行完了之后,再注册的then,then就不能执行了,所以我们改一版

resolve在then之后执行的版本

function Promise(fn) {
    var value = null,
        callbacks = [];  //callbacks为数组,因为可能同时有很多个回调

    this.then = function (onFulfilled) {
        callbacks.push(onFulfilled);
    };

    function resolve(value) {
       setTimeout(function() {
        callbacks.forEach(function (callback) {
            callback(value);
        });
    }, 0)
    }

    fn(resolve);
}

这里用到了setTimeout(fn,0)的技巧,把resolve放在队列的最后去执行,这样就实现了then->resolve这样的流程了。

状态改变不可变更版本

我们都知道,promise 实例有3个状态,如下图,从pending到fulfilled和rejected都是不可逆的。

我们就加入状态吧

function Promise(fn) {
    var state = 'pending',
        value = null,
        callbacks = [];

    this.then = function (onFulfilled) {
        if (state === 'pending') {
            callbacks.push(onFulfilled);
            return this;
        }
      
        return this;
    };

    function resolve(newValue) {
        value = newValue;
        state = 'fulfilled';
        setTimeout(function () {
            callbacks.forEach(function (callback) {
                callback(value);
            });
        }, 0);
    }

    fn(resolve);
}

上述代码的思路是这样的:resolve执行时,会将状态设置为fulfilled,之后添加的then就不执行了。

总结

上面能实现一个基本的Promise,只是为了讲原理,其实就是闭包+函数回调+单线程这些的组合,搞出一个异步解决方案。当然还有失败处理啊,异常处理啊这些东西,原理都差不多。