对Promise的理解

对Promise的理解,主要是对Promise源代码分析展开(附上github链接https://github.com/then/promise):
Promise 对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的Promise对象。
先来看个简单例子:

var pro = new Promise(function(res,rej){
    setTimeout(function(){
        console.log('1s execute...');
        res(1)
    },1000)
})
.then(function(n){
    console.log(n)
 })

从上面例子可以看出,promise接受一个函数,这个函数接受2个参数,分别负责来实现异步行为之后的回调操作。
接下来,我们来看看源代码:

function Promise(fn) {
  if (typeof this !== 'object') {
    throw new TypeError('Promises must be constructed via new');
  }
  if (typeof fn !== 'function') {
    throw new TypeError('Promise constructor\'s argument is not a function');
  }
  this._deferredState = 0;
  this._state = 0;
  this._value = null;
  this._deferreds = null;
  if (fn === noop) return;
  doResolve(fn, this);
}

上面这部分代码其实就是接受函数,并执行promise里面传的参数,重点在doResolve这个方法,看这个方法之前先看3个基本的工具方法:

//  获取某个对象的then属性
function getThen(obj) {
  try {
    return obj.then;
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}
//  执行调用fn,并传入一个参数
function tryCallOne(fn, a) {
  try {
    return fn(a);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}
//  执行调用fn,并传入两个参数
function tryCallTwo(fn, a, b) {
  try {
    fn(a, b);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

这时候,再来看一下doResolve这个方法:

function doResolve(fn, promise) {
  var done = false;
  var res = tryCallTwo(fn, function (value) {
    if (done) return;
    done = true;
    resolve(promise, value);//   将当前promise和resolve 的value传进去
  }, function (reason) {
    if (done) return;
    done = true;
    reject(promise, reason);
  });
  if (!done && res === IS_ERROR) {
    done = true;
    reject(promise, LAST_ERROR);
  }
}

这个方法主要就是执行了新建promise对象时候传的函数,我们仔细看第2、3个参数,这2个函数执行的时候,会分别进入resolve和reject方法里面,我们以resolve为例,来看看内部的resolve干了什么:

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 (
    newValue &&
    (typeof newValue === 'object' || typeof 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;  //  如果value本身是一个promise对象,则状态跟着这个promise走
      self._value = newValue;
      finale(self);
      return;
    } else if (typeof then === 'function') {   //  如果value是一个带then函数的对象,则继续走doResolve(基于then函数)
      doResolve(then.bind(newValue), self);
      return;
    }
  }
//  如果value没啥特殊的,就走正常程序
  self._state = 1; //  state变为1
  self._value = newValue; 
  finale(self);
}

我们顺着最简单的情况看流程哈,因为任何特殊情况,走到最后都是最简单的情况。这样,接着看finale函数:

function finale(self) {
  //  简单意思便是:一个一个执行promise对象队列中的deferreds
  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;
  }
}

怎么突然冒出来个_deferreds来了呢,其实_deferreds可以理解为当前异步执行队列中的处理器,那么它的赋值是在哪里做的呢?我们来看下promise的then方法的代码:

Promise.prototype.then = function(onFulfilled, onRejected) {
  if (this.constructor !== Promise) {
    return safeThen(this, onFulfilled, onRejected);
  }
  var res = new Promise(noop);  //  then方法,新建一个promise
  handle(this, new Handler(onFulfilled, onRejected, res));  //  并将新建一个handler,然后再进行处理
  return res;
};

function safeThen(self, onFulfilled, onRejected) {
  return new self.constructor(function (resolve, reject) {
    var res = new Promise(noop);
    res.then(resolve, reject);
    handle(self, new Handler(onFulfilled, onRejected, res));
  });
}

我们先来看下Handler构造函数干了啥:

function Handler(onFulfilled, onRejected, promise){
  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  this.promise = promise;
//  新建了一个对象,promise属性存储当前传进来的promise对象,onfulfilled/onRejected分别存储then方法传进来的回调函数
}

这样,结合上面,我们可以发现then的时候,将对应的handler放到了handle方法中去处理,我们看看handle方法:

function handle(self, deferred) {
  while (self._state === 3) {
    //  当state为3的时候,则整个状态依赖于新的那个promise类型的value
    self = self._value;
  }
  if (Promise._onHandle) {
    Promise._onHandle(self);
  }
  if (self._state === 0) {//  当还是0的时候,往_deferreds里面放处理器(也就是then方法执行的逻辑点)
    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
  handleResolved(self, deferred);
}

来看看handleResolved源代码:

function handleResolved(self, deferred) {
  asap(function() {
    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;
    }
    var ret = tryCallOne(cb, self._value);  //  将值传人回调函数,并返回执行结果
    if (ret === IS_ERROR) {
      reject(deferred.promise, LAST_ERROR);
    } else {
      resolve(deferred.promise, ret);  // 再将当前结果resolve运行到then返回的promise中去 
    }
  });
}

到此为止,咱们已经把promise的主要核心代码已经分析过一遍了。仔细想想,其实主要就围绕着以下几个关键点展开:
1、执行传到promise构造函数的函数;
2、根据then和handle方法结合,将需要处理的handlers放在处理队列之中;
3、内部实现的resolve和reject方法分别更新当前promise对象的状态;
4、根据状态来执行对应的处理函数;
5、循环上述过程。

下面看几个实际例子来配合理解:

=========================
//  例子1:简单传值
var pro = new Promise(function(res,rej){
     setTimeout(function(){
          console.log('1s execute...')
          res(1)
      },1000)
});
pro.then(function(n){
    console.log(n)
})
//  例子1输出结果:
1s execute...
1
=========================
//  例子2: 连续传递,且第1个then方法不返回值
var pro = new Promise(function(res,rej){
     setTimeout(function(){
          console.log('1s execute...')
          res(1)
      },1000)
});
pro.then(function(n){
    console.log(n)
}).then(function(m){
    console.log(m)
})
//  例子2输出结果:
1s execute...
1
undefined
=========================
//  例子3:连续传递,且第1个then方法正常返回值
var pro = new Promise(function(res,rej){
     setTimeout(function(){
          console.log('1s execute...')
          res(1)
      },1000)
});
pro.then(function(n){
    console.log(n);
    return 2;
}).then(function(m){
    console.log(m)
})
//  例子3输出结果:
1s execute...
1
2
=========================
//  例子4:连续传递,且第1个then方法返回promise,但是不res更改状态,会中断传递
var pro = new Promise(function(res,rej){
     setTimeout(function(){
          console.log('1s execute...')
          res(1)
      },1000)
});
pro.then(function(n){
    console.log(n);
    return new Promise(function(res,rej){
    setTimeout(function(){
    console.log('promise waiting')
    },1000)
    })
}).then(function(m){
    console.log(m)
})
//  例子4输出结果:
1s execute...
1
promise waiting
=========================
//  例子5:连续传递,且第1个then方法返回promise,但是不res更改状态,不会中断传递
var pro = new Promise(function(res,rej){
     setTimeout(function(){
          console.log('1s execute...')
          res(1)
      },1000)
});
pro.then(function(n){
    console.log(n);
    return new Promise(function(res,rej){
    setTimeout(function(){
        console.log('promise waiting');
        res('promise 传递');
     },1000)
    })
}).then(function(m){
    console.log(m)
})
//  例子5输出结果:
1s execute...
1
promise waiting
promise 传递
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341