Promise-resolve流程源码剖析

参考自:https://segmentfault.com/a/1190000012500519?from=timeline

1. 最简单的一个案例

function runAsync() {
  let p = new Promise(function(resolve, reject) {
    console.log('exec');
    setTimeout(function() {
      reslove('someData');
    }, 2000);
  });
  return p;
}
var promise = runAsync();
promise.then(function(data) {
  console.log(data);
});
console.log('同步执行');
console.log(promise);

2. Promise内部是如何运行的?

2.1 执行这行代码的时候:let p = new Promise(f);
function noop() {};
function Promise(fn) {
  if (typeof this !== 'object') {
    throw new TypeError('Promise must be consructed via new');
  }
  if (typeof fn !== 'function') {
    throw new TypeError('Promise consructor's argument is not a function);
  }
  this._deferredState = 0;
  this._state = 0;
  this._value = null;
  this._deferreds = null;
  //  注意这里,如果fn传入的是noop这个函数,那么不会执行doResolve
  if(fn === noop) return;
  doResolve(fn, this);
}
Promise._onHandle = null;
Promise._onReject = null;
Promise._noop = noop;
function doResolve(fn, promise) {
  var done = false;
  var res = tryCallTwo(fn, function(value) {
    if (done) return;
    done = true;
    resolve(promise, value);
  }, function(reason) {
    if (done) return;
    done = true;
    reject(promise, LAST_ERROR);
  });
}
function tryCallTwo(fn, a, b) {
  try {
    fn(a, b);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

可以看到let p = new Promise(f); 在传入的f函数不是noop的时候:

  • 第一会先执行f函数
  • 第二生成一个Promise对象
p => {
  _deferredState : 0, //  deffer的状态,代表的应该是_deferreds的类型:1代表single,2代表Array
  _state : 0, //  每个promise对象的状态维护标识
  _value : null,  //  resolve函数执行的时候,异步得到的结果
  _deferreds : null 
}
2.2 执行这行代码的时候:promise.then(function(data){console.log(data);});
Promise.prototype.then = function(onFulfilled, onRejected) {
  if (this.constructor !== Promise) { // 这些比较的都是引用地址;
    return safeThen(this, onFulfilled, onRejected);
  }
  var res = new Promise(noop);
  handle(this, new Handler(onFulfilled, onRejected, res));
  //  注意这里then的链式调用,每次then函数执行完毕之后,返回值都是一个新的Promise实例对象
  return res;
};
//  这里生成一个deferred对象,保存then函数中注册的onFulfilled和onRejected回调,以及要返回的新的promise实例对象
function Handler(onFulfilled, onRejected, promise) {
  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  this.promise = promise;
}
function handle(self, deferred) {
  //  不会进入这里
  while (self._state === 3) {
    self = self._value;
  }
  //  也不会进入这里
  if (Promise._onHandle) {
    Promise._onHandle(self);
  }
  //  进入这里,注意这里,如果通过then的链式调用,那么每次then返回的对象都是一个新的类似于下面p的实例对象
  if (self._state === 0) {
    if (self._deferredState === 0) {
      self._deferredState = 1;
      self._deferreds = deferred;
      return;
      //  第一次执行到这里即结束
    }
    if (self._deferredState === 1) {
      self._deferredState = 2;
      self._deferreds = [self._deferreds, deferred];
      return;
    }
    self._deferreds.push(deferred);
    return;
  }
  //  这个函数只有当_state的值为1 2的时候才会执行
  handleResolved(self, deferred);
}

此时再来看下p这个promise实例的属性值

p => {
  _.deferredState: 1, //  deferreds的状态
  _.state: 0, //  每个promise对象的状态维护标识
  _.value: null,  //  resolve函数执行的时候,异步得到的结果
  _.deferreds: new Handler(onFulfilled, onRejected, res); //  存放通过then注册的函数以及返回Promise实例对象的一个Handler对象
}
res: {
  _deferredState: 0,  //  deferreds的状态
  _state: 0,  //  每个promise对象的状态维护标识
  _value: null,   //  resolve函数执行的时候,异步得到的结果
  _deferreds: null
}

如果返回的res在执行then方法,那么

p => {
  _.deferredState: 1, //  deferreds的状态
  _.state: 0, //  每个promise对象的状态维护标识
  _.value: null,  //  resolve函数执行的时候,异步得到的结果
  _.deferreds: new Handler(onFulfilled, onRejected, res); //  存放通过then注册的函数以及返回Promise实例对象的一个Handler对象
}
res: {
  _deferredState: 1,  //  deferreds的状态
  _state: 0,  //  每个promise对象的状态维护标识
  _value: null,   //  resolve函数执行的时候,异步得到的结果
  _deferreds: new Handler(onFulfilled, onRejected, res);  //  存放通过then注册的函数以及返回的Promis实例对象的一个Handler对象
}
2.3 异步操作完成以后:resolve('someData');
//  真正调用这个函数的是tryCallTwo中的第二个函数入参数。
//  self就是p这个promise实例对象。
function resolve(self, newValue) {
  //  Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
  //  对于此时的案例,不会进入这里
  if (newValue === self) {
    return reject(
      self,
      new TypeError('A promise cannot be resolved with itself.');
    );
  }
  //  也不会进入这里
  if (
    //  比如resolve(p1),p1是一个新的promise对象
    newValue &&
    (typeof newValue === 'object' || typepf newValue === 'function')
  ) {
    var then = getThen(newValue);
    if (then === IS_ERROR) {
      return reject(self, LAST_ERROR);
    }
    if (
      then === self.then &&
      newValue instanceof Promise
    ) {
      self._state = 3;
      self._value = newsValue;
      finale(self);
      return;
    } else if (typeof then === 'function') {
      doResolve(then.bind(newValue), self);
      return;
    }
  }
  //  对于简单的返回值,比如后台返回的JSON字符串等,这里就直接进行处理
  self._state = 1;  //  fulfilled
  self._value = newsValue;  //  给这个promise对象添加属性值_value,用来保存异步操作的结果
  finale(self); //  最后处理这个promise对象
}

此时的promise实例对象,关注对比p这个实例对象的变化,可以看到resolve之后,相当于将异步的结果给到了p这个Promise实例对象的_value属性值,同时改变这个p的状态_state为1 ==> fulfilled

p => {
  _deferredState: 1,  //  deferred的状态
  _state: 1,  //  每个promise对象的状态维护标识
  _value: 'someDate', //  resolve函数执行的时候,异步得到的结果
  _deferreds: new Handler(onFulfilled, onRejected, res);
}
2.4 finale(self); // 最后处理这个promise对象
function finale(self) {
  //  进入这个if语句
  if (self._deferredState === 1) {
    handle(self, self._deferreds);
    self._deferreds = null;
  }
  if (self._deferredState === 2) {
    for (var i = 0; i < self._deferreds.length; i++) {
      handle(self, self._deferreds[i]);
    }
    self._deferreds = null;
  }
}
function handle(self, deferred) {
  while (self._state === 3) {
    self = self._value;
  }
  if(Promise._onHandle) {
    Promise._onHandle(self);
  }
  //  此时不会进入这里因为_state的值为 1
  if (self._state === 0) {
    if (self._deferredSate === 0) {
      self._deferredSate = 1;
      self._deferreds = deferred;
      return;
    }
    if (self._deferredSate === 1) {
      self._deferredSate = 2;
      self._deferreds = [self._deferreds, deferred]];
      return;
    }
    self._deferreds.push(deferred);
    return;
  }
  //  这个函数只有当_state的值为
  //  1即:fulfilled.
  //  2即:rejected.
  //  的时候才执行
  handleResolved(self, deferred);
}
function handleResolved(self, deferred) {
  asap(function() {
      //  得到promise.then(function(data){console.log(data);}); 注册的函数
      var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
      if (cb === null) {
        if (self._state === 1) {
          resolve(deferred.promise, self._value);
        } else {
          reject(deferred.promise, self._value);
        }
        return;
      }
      //  执行then中注册的回调函数
      var ret = tryCallOne(cb, self._value);
      if (ret === IS_ERROR) {
        reject(deferred.promise, LAST_ERROR);
      } else {
        resolve(deferred.promise, ret);
      }
  });
};
function tryCallOne(fn, a) {
  try {
    return fn(a);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

至此一个简单的promise的实现流程完毕:
以下面实例来看一下:

3 对于then的链式调用,Promise内部又是如何运行的呢?

下一个then中注册的函数会接收到上一个then的返回值作为该then中注册的函数的参数;

3.1 then注册的函数返回基本数据类型
function runAsync() {
    let p = new Promise(function(resolve, reject) {
      console.log('exec');
      setTimeout(function() {
        resolve('someData');
      }, 2000);
    });
    return p;
}
var promise = runAsync();
promise.then(function(data) {
  console.log(data);
  return 'someData1';
}).then(function(data) {
  console.log(data);
  return 'someData2';
}).then(function(data) {
  console.log(data);
});
console.log('同步执行');
console.log(promise);

控制台输出:

exec
同步执行
Promise
//  两秒后
someData
someData1
someData2

resolve(deferred.promise, ret);中的ret值就是then中注册函数的返回值,这里就是一些简单的字符串'someData1''someData2'

promise实例对象==> 异步成功 ==> 该实例对象的resolve(data) ==> 
//newValue为异步得到的数据,第一次是'someData'这个字符串,下一次就是then中注册函数的返回值ret,还是字符串'someData1' 'someData2' 
resolve(self,newValue)     ==>            <== == == == ==  == ==  ||
                                                                ^
==>handle(self,deffer)                                             ||
                                                                ^    
==>handleResolved处理then中注册的函数;                              ||
                                                                ^
==>接着处理下一个promis==>resolve(deferred.promise, ret);    ===> ||
var p = {
  _deferredState: 1,  // deferred的状态
  _state: 0,  //  每个promise对象的状态维护标识
  _value: null, //  resolve函数执行的时候,异步得到的结果
  _deferred: {
    onFulfilled: onFulfilled,
    onRejected: onRejected,
    promise: {  // 这里使new Promise(noop)
      _deferredState: 1,
      _state: 0,
      _value: null,
      _deferreds: { //  通过then注册的执行对象
        onFulfilled: onFulfilled,
        onRejected: onRejected,
        promise: {  //  这里是:new Promise(noop)
          _deferredState: 1,
          _state: 0,
          _value: null,
          _deferreds: { //  通过then注册的执行对象
            onFulfilled: onFulfilled,
            onRejected: onRejected,
            promise: {  //   这里使new Promise(noop)
              _deferredState: 1,
              _state: 0,
              _value: null,
              _deferreds: { //  通过then注册的执行对象
                onFulfilled: onFulfilled,
                onRejected: onRejected,
                promise: {  //  这里使new Promise(noop)
                  _deferredState: 1,
                  _state: 0,
                  _value: null,
                  _deferreds: null
                }
              }
            }
          }
        }
      }
    }
  }
};
3.2 then注册的函数返回一个新的promise
function runAsync() {
  let p = new Promise(function(resolve, reject) {
    console.log('exec');
    setTimeout(function() {
      resolve('someData');
    }, 2000);
  });
  return p;
};
function runAsync1() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('someData1');
    },2000);
  });
};
function runAsync2() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('someData2');
    }, 2000);
  });
};
//  以下的异步操作会按照顺序进行执行
var promise = runAsync();
promise.then(function(data) {
  console.log(data);
  return runAsync1();
}).then(function(data) {
  console.log(data);
  return runAsync2();
}).then(function(data) {
  console.log(data);
  //  return 'someData3'
})
console.log('同步执行');
console.log(promise);
exec
同步执行
Promise
//  两秒后
someData
//  两秒后
someData1
//  两秒后
someData2
promise实例对象 ==> 异步成功 ==> 该实例对象的resolve(data) ==> 
//newValue为异步得到的数据,这里第一次是 'someData'这个字符串,下一次就是then中注册函数的返回值,这里就是runAsync返回的promise对象
resolve(self,newValue)    ==>            <== == == == ==  == ==  ||
                                                                ^
==>handle(self,deffer)                                             ||
                                                                ^
==>handleResolved处理then中注册的函数;                              ||
                                                                ^
==>接着处理下一个promise==>resolve(deferred.promise, ret);    ===> ||

整个promise链如下:

var p = {
  _deferredState: 1,  //  deferred的状态
  _state: 0,  //  每个promise对象的状态维护标识
  _value: null, //  resolve函数执行的时候,异步得到的结果
  _deferred: {
    onFulfilled: onFulfilled,
    onRejected: onRejected,
    promise: {  //  通过引用的地址改变,这里使runAsync返回的promise
      _deferredState: 1,
      _state: 0,
      _value: null,
      _deferreds: { //  由于runAsync中没有执行then注册,这里将new Promise(noop)通过then注册的对象引用拿到
        onFulfilled: onFulfilled,
        onRejected: onRejected,
        promise: {  //  通过引用的地址改变,这里是runAsync返回promise
          _deferredState: 1,
          _state: 0,
          _value: null,
          _deferreds: null
        }
      }
    }
  }
}
3.3 以上两者之间的差异性
  • 相同点:每个then返回的新promise对象是一样的,都是通过then函数中定义的返回值:var res = new Promise(noop);
  • 不同点:在handleResolved中,resolve(deferred.promise, ret)中的ret的值不同,ret就是每个then中注册的函数的返回值,对比以上两种情况,一个返回基本数据类型,一个返回Promise对象,接下来重点看下then中注册的函数返回promise对象的情况(注意这个和then链式调用的promise对象不是一个)
//  resolve(deferred.promise, ret);
//  注意这个self,传入的是deferred这个对象中的promise这个引用地址;
//  真正调用这个函数的是tryCallTwo中的第二个函数入参;
//  self就是p这个promise实例对象;
function resolve(self, newValue) {
  // 此时的newValue是then中注册函数所返回的Promise实例对象 
  // self是deferred.promise
  // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure 
  // 对于此时的案例,不会进入这里
  if (newValue === self) {
    return reject(
      self,
      new TypeError('A promise cannot be resolved with itself.');
    );
  }
  // 注意,对于then中注册的函数返回值是一个新的promise对象的时候,此时会进入这里
  if (
    //  比如resolve(p1) p1四一个新的promise对象;
    newValue &&
    (typeof newValue === 'object' || typeof newValue === 'function')
  ) {
    var then = getThen(newValue);
    if (then === IS_ERROR) {
      return reject(self, LAST_ERROR);
    }
    if (
      //  两个Promise对象原型链上都是采用的then这个函数地址
      then === self.then &&
      newValue instanceof promise
    ) {
      self._state = 3;
      self.value = newValue;
      finale(self);
      //  执行到这里,结束;
      return;
    } else if (typeof then === 'function') {
      doResolve(then.bind(newValue), self);
      return;
    }
  }
  //  对于then中注册的函数返回一个promise对象的情况,下面就不会执行
  self._state = 1;
  self._value = newValue;
  finale(self);
}

self ==> deferred.promise

function finale(self) {
  if (self._deferredState === 1) {
    handle(self, self._deferreds);
    self._deferreds = null;
  }
  if (self._deferredState === 2) {
    for (var i = 0; i < self._deferreds.length; i++) {
      handle(self, self._deferreds[i]);
    }
    self._deferreds = null;
  }
}

注意这里通过对promise链进行引用的改变,从而使异步的执行看起来和同步是一样的;
handle函数有两个作用:

  • 第一:改变promise链的引用,将原本返回的new Promise(noop)改为ret(then)中注册函数返回的promise。
  • 第二:将原本new Promise(noop)上面通过then注册的deferred对象,给到ret响应的属性。
function handle(self, deferred) {
  while (self._state === 3) {
    //  这里一直循环直到取到我们返回的promise对象,也就上面的ret,即每个runAsync函数的返回值;
    self = self._value;
  }
  if (Promise._onHandle) {
    Promise._onHandle(self);
  }
  //  将runAsync返回的promise对象中_deferredState设置为1;
  if (self._state === 0) {
    if (self._deferredState === 0) {
      self._deferredState = 1;
      self._deferreds = deferred;
      //  执行到这里结束
      return;
    }
    if (self._deferredState === 1) {
      self._deferredState = 2;
      self._deferreds = [self._deferreds, deferred];
      return;
    }
    self._deferreds.push(deferred);
    return;
  }
  handleResloved(self, deferred);
}

4. 综合练习

对于注释的代码可以来回切换,看下结果。

function runAsync() {
  let p = new Promise(function(resolve, reject) {
    console.log('exec');
    setTimeout(function() {
      resolve('someData');
    }, 2000);
  });
  return p;
};
function runAsync1() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      console.log('异步1');
      //  resolve('someData1');
      reject('error1');
    }, 2000);
  })
};
function runAsync2() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      console.log('异步2');
      //  resolve('someData2');
      reject('error2');
    }, 2000);
  })
};
var promise = runAsync();
promise.then(function() {
  console.log(data);
  return runAsync1();
}).then(function() {
  console.log(data);
  return runAsync2();
}).then(function() {
  console.log(data);
  //  return 'someData3'
}).catch(function(error) {
  console.log(error);
});
console.log('同步执行');
console.log(promise);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,653评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,321评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,833评论 0 324
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,472评论 1 266
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,306评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,274评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,658评论 3 385
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,335评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,638评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,697评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,454评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,311评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,699评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,986评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,254评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,647评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,847评论 2 335

推荐阅读更多精彩内容

  • afinalAfinal是一个android的ioc,orm框架 https://github.com/yangf...
    passiontim阅读 15,363评论 2 44
  • 作为一个前端程序猿,下面这些站会让你眼前一亮。 amazeui框架组建丰富 http://amazeui.org...
    欧巴冰冰阅读 8,798评论 18 303
  • 1. 浮动元素有什么特征?对父容器、其他浮动元素、普通元素、文字分别有什么影响? 浮动元素:元素脱离正常的文档流,...
    lingfighting阅读 790评论 1 4