es6 promise源码实现

20

promise源码分析

初级入门以及如何使用请看 阮一峰promise对象讲解

先上一坨代码,后面我们要基于这坨代码来实现自定义promise

原始方法

setTimeout(function(){
    var a=100;
    console.log(a);
    setTimeout(function () {
        var b=200;
        console.log(b)
        setTimeout(function () {
            var c=300;
            console.log(c)
        }, 1000);
    }, 1000);
},1000);   

promise实现

 new Promise(function (resolve, reject) {
    setTimeout(function () {
        var a=100;
        resolve(a);
    }, 1000);
}).then(function (res) {
    console.log(res);
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            var b=200;
            resolve(b);
        }, 1000);
    })
}).then(function (res) {
    console.log(res);
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            var c=300
            resolve(c);
        }, 1000);
    })
}).then(function (res) {
        console.log(res);
    }
    )

如何让你的promise能有此魔力

  • 让a,b,c的值能在then里面的回调接收到
  • 在连续调用异步,如何确保异步函数的执行顺序

如何让异步的value在thenable函数中拿到

  • 将resolve/reject函数和onfulfiled/onrejected放入同一个对象(promise对象)里面,resolve/reject的时候将value设置this.value=xxx。onfulfiled/onrejected执行的时候呢,onfulfiled(this.value)即可

如何处理链式的promise且保证顺序

  • 每个promise后面链一个对象该对象包含onfulfiled,onfulfiled,子promise三个属性.

当父promise 状态改变完毕,执行完相应的onfulfiled/onfulfiled的时候呢,拿到子promise,在等待这个子promise状态改变,在执行相应的onfulfiled/onfulfiled。依次循环直到当前promise没有子promise

最终代码(内涵注释)

            /*
            我们要满足状态只能三种状态:PENDING,FULFILLED,REJECTED三种状态,且状态只能由PENDING=>FULFILLED,或者PENDING=>REJECTED
            */
            var PENDING = 0;
            var FULFILLED = 1;
            var REJECTED = 2;
            /*
            value状态为执行成功事件的入参,deferreds保存着状态改变之后的需要处理的函数以及promise子节点,构造函数里面应该包含这三个属性的初始化
             */
            function Promise(callback) {
                this.status = PENDING;
                this.value = null;
                this.defferd = [];
                setTimeout(callback.bind(this, this.resolve.bind(this), this.reject.bind(this)), 0);
            }
            
            Promise.prototype = {
                constructor: Promise,
                //触发改变promise状态到FULFILLED
                resolve: function (result) {
                    this.status = FULFILLED;
                    this.value = result;
                    this.done();
                },
                //触发改变promise状态到REJECTED
                reject: function (error) {
                    this.status = REJECTED;
                    this.value = error;
                },
                //处理defferd
                handle: function (fn) {
                    if (!fn) {
                        return;
                    }
                    var value = this.value;
                    var t = this.status;
                    var p;
                    if (t == PENDING) {
                         this.defferd.push(fn);
                    } else {
                        if (t == FULFILLED && typeof fn.onfulfiled == 'function') {
                            p = fn.onfulfiled(value);
                        }
                        if (t == REJECTED && typeof fn.onrejected == 'function') {
                            p = fn.onrejected(value);
                        }
                    var promise = fn.promise;
                    if (promise) {
                        if (p && p.constructor == Promise) {
                            p.defferd = promise.defferd;
                        } else {
                            p = this;
                            p.defferd = promise.defferd;
                            this.done();
                        }
                    }
                    }
                },
                //触发promise defferd里面需要执行的函数
                done: function () {
                    var status = this.status;
                    if (status == PENDING) {
                        return;
                    }
                    var defferd = this.defferd;
                    for (var i = 0; i < defferd.length; i++) {
                        this.handle(defferd[i]);
                    }
                },
                /*储存then函数里面的事件
                返回promise对象
                defferd函数当前promise对象里面
                */
                then: function (success, fail) {
                   var o = {
                        onfulfiled: success,
                        onrejected: fail
                    };
                    var status = this.status;
                    o.promise = new this.constructor(function () {
            
                    });
                    if (status == PENDING) {
                        this.defferd.push(o);
                    } else if (status == FULFILLED || status == REJECTED) {
                        this.handle(o);
                    }
                    return o.promise;
                }
            };

在附上一张手绘的流程图

图片描述

参考资料

源码地址

你可能感兴趣的

21 条评论
朱建 作者 · 2016-09-17

hi,没怎么看懂你的问题,我是否能理解为你说多个then的时候只调用了一个onfulfiled/onrejected呢?
OK,onfulfiled从属于fn对象,fn对象则是上一个promise的运行完相应onfulfiled/onrejected的返回值(别名a),a应该属于promise对象,如果不是则直接链上上一个promise。

+2 回复

决意 · 2016-12-07

思路不错,但是貌似系统的promise不是这样的,因为你这是一开始用timeout注册了一个事件,把callback的时间延后到了主线程之后,可系统的没有

+1 回复

1

如果是node系统,用的不是setTimeout(fn,0),而是process.nextTick(),这个函数是添加到主线程tick末尾。不知道你说的是不是这个意思。

_小刀 · 2017-03-22
0

哦哦,我看到了,官方实现的确和楼主的不一样,一开始确实不用setTimeout,官方实现是直接调用。

_小刀 · 2017-03-22
1

@_小刀 意思是差不多,不过系统的也不是process.nextTick(),而是一个PromiseJob,从属于es6 的Job Queue,类似于Event Loop Queue的 nextTick。

朱建 作者 · 2017-03-23
朱建 作者 · 2016-12-08

是的,promise官网没在初始化的时候timeout,但是在done函数里面用了。官网每次new Promise只会初始化一次和调用一次done,所以感觉差不多吧:)

+1 回复

aceleewinnie · 2016-09-13

手绘流程图部分还是比较明确的,但是三个如何部分没太看懂,是不是有些错误,可以说的再细一些吗

回复

朱建 作者 · 2016-09-14

hi
第一个"如何"是阐述2个问题:
1.a是function (resolve, reject) {var a=100;resolve(a);}里面的,要将a的值传递到then(function (res) {console.log(res)}的res里
2.连续调用异步 promise.then(callback).then(callbac)意思是如何在一直调用then函数时候 如何保证第一个callback比第二个优先执行
第二个"如何"是解决第一个问题:
通过将resolve/reject和对应的onfulfiled/onrejected函数的作用域放在一个对象内,从而共享a的值
第二个"如何"是解决第二个问题:
简言之通过递归,形成一个树状结构,每个节点有三个属性onfulfiled,onfulfiled,子promise 来保证执行顺序问题:)

回复

mengdu · 2016-09-17

这个好像有问题,多个then的时候,回调中的才是既然是同一个,这里应该错了吧

回复

水影心 · 2018-01-17

你好,作者。很感谢你的代码让我理解了promise的初步原理,多谢了。
想请教你1个问题,代码如下:
我在你代码上打了些端点。为什么constructor里的端点总是比下面2个端点最后执行,这点我不太明白。
then: function (success, fail) {

        var o = {
            onfulfiled: success,
            onrejected: fail
        };
        var status = this.status;
        o.promise = new this.constructor(function () {
            debugger;
        });
        if (status == PENDING) {
            this.defferd.push(o);
            debugger;
        } else if (status == FULFILLED || status == REJECTED) {
            this.handle(o);
            debugger;
        }
        debugger;
        return o.promise;
    }

回复

0

抱歉,年前比较忙,没看回复。
constructor 是Promise构造函数上面运行的是回调函数,

setTimeout(callback.bind(this, this.resolve.bind(this), this.reject.bind(this)), 0);

你可以理解成多个链式的then函数是第一时间执行初始化,而settimeout的回调函数在其后执行。

朱建 作者 · 2018-03-06
健儿 · 2018-03-01

我很疑惑,网上都找不到promise 真正的源码。求一个url地址啊,多谢了。

回复

0

https://chromium.googlesource... 谷歌很容易找的,不过这个网址一般需要翻墙的

朱建 作者 · 2018-03-04
健儿 · 2018-03-01

reject() 函数少了一行this.done() 代码。否则then函数的第二个参数无法执行。

回复

0

是的,当时只是专注在resovle上了。

朱建 作者 · 2018-03-06
Yhspehy · 2018-04-24

关于defferd我有点不理解,我现在对this.defferd,p.defferd,fn.promise.defferd弄不清楚,楼主能给我讲解一下吗

回复

tricksiter · 2018-06-04

你好,在handle中
if (t == PENDING) {

  this.defferd.push(fn);

}
实际上不会执行吧?handle的进入条件就是当前promise的状态不是pending?从代码逻辑上和执行顺序上没有发现这个分支有可能被经过,辛苦介绍一下~

回复

0

确实重复了,这块逻辑和87行内的then 函数逻辑重复了

 if (status === PENDING) {
      this.defferd.push(o);
    }
朱建 作者 · 1月10日
hxkuc · 2018-12-29

为什么楼主你写的那么复杂呢,而我写的这么简单呢,是不是我哪里没有想到??,能不能帮我看看代码,我只写了resolve的部分

          class Pro {
           constructor (fun) {
            this.PromiseValue = ''
            this.PromiseStatus = 'pending'
            this.PromiseList = []
            if (fun) {
              fun(this.resolve.bind(this), this.reject.bind(this))
            }
           }
           then (fun) {
            this.PromiseList.push(fun)
            return this
           }

           resolve (v) {
            this.PromiseStatus = 'resolved'
            this.PromiseValue = v
            this.go()
           }

           reject (v) {
            this.PromiseStatus = 'rejected'
            this.PromiseValue = v
            this.go()
           }

           // 递归函数
           go () {
             if (this.PromiseStatus !== 'pending' && this.PromiseList.length) {
              const a = this.PromiseList.shift()(this.PromiseValue)
              if (a instanceof Pro) {
                a.PromiseList = this.PromiseList
                a.go()
              } else {
                this.PromiseValue = a
                this.go()
              }
             }
           }
         }

回复

0

new Pro(function (resolve, reject) {
  setTimeout(function () {
    var a = 100;
    resolve(a);
  }, 1000);
}).then(function (res) {
  console.log(res);
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      var b = 200;
      resolve(b);
    }, 1000);
  })
}).then(function (res) {
  console.log(res);
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      var c = 300
      resolve(c);
    }, 1000);
  })
}).then(function (res) {
    console.log(res);
  }
);

这个例子你试试就知道了,你用原生的Promise 跑跑就知道了。

你的then 不支持递归promise的

朱建 作者 · 1月10日
0

你把原生的Promise换成我的Pro就可以了,我只是简单写了一下,没考虑兼容promise

hxkuc · 1月11日
载入中...