2018-03-18

Promise初次尝试

注意:判断一个变量是不是Promise,有三项主要的特征可作为参考。

  • 该变量有三种状态pendingfulfilledrejected
  • 该变量 含有then方法
  • 该变量含有Promise Resolution Procedure (promise的状态转换处理方法)。

1.promise基本使用

let p = new Promise((resolve,reject)=>{
  let x = Math.random();
  console.log(x);
  if (x > .5) {
    resolve('666');
  } else {
    reject('GG思密达');
  }
});

p.then((value)=>{ //绑定成功时的回调函数
  console.log('fulfilled:',value); 
},(reason)=>{ //绑定失败时的回调函数
  console.log('rejected:',reason); 
});

当Promise处于pending状态时,它可能转换为fulfilled或则rejected状态。

当Promise处于fulfilled状态时,它不再能转换为其它状态 且 它必须有一个值,这个值不能被更改。

当promise处于rejected时,它不再能转换为其它状态 且 它必须有一个理由,这个理由不能被更改。

2.promise的基本实现

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function Promise(executor){
  let self = this; //缓存下
  self.value = undefined; //用来存放value和reason,因为promise只会处于一种状态故可只用一个变量来表示。
  self.status = PENDING; //将初始状态设置为pending
  self.onFulfilledCallbacks = []; //用来存放所有成功的回调函数
  self.onRejectedCallbacks = []; //用来存放所有失败的回调函数

  try{
    executor(resolve,reject); //调用执行函数,将resolve和reject方法作为参数传入
  }catch (e){
    reject(e); //若执行函数中存在异常直接用抛出的值来拒绝promise
  }
  //-----------------------------------------------------------------------------------------------------------
  function resolve(value){ //此方法会随着executor传入而传入
    setTimeout(function(){
      if(self.status === PENDING){ //确保状态只会改变一次
        self.status = FULFILLED; //改变状态
        self.value = value; //赋予一个值
        self.onFulfilledCallbacks.forEach(cb => cb(self.value)); //2.2.2. //2.2.6.
      }
    })
  }
  function reject(reason){
    setTimeout(function(){
      if(self.status === PENDING){
        self.status = REJECTED;
        self.value = reason;
        self.onRejectedCallbacks.forEach(cb => cb(self.value));
      }
    })
  }
}

/*我们可以发现最终转换状态时通过Promise内部的两个方法resolve和reject,这个两个方法是在什么时候传入的呢?一个函数的参数查找,是从调用这个函数时所处的作用域开始查找的。new Promise传入的executor,是参数也是对executor函数的定义,此时executor的resolve和reject为形参。我们new Promise的时候,会执行构造函数Promise内的代码,也就是在这时executor被执行,而executor此时所处的作用域是在Promise构造函数内部,resolve和reject方法作为实参被传入。*/

2.1. then方法

  • 一个promise必须提供一个then方法来使用它将要或则说已经被赋予的 value 或则 reason,一个promise的then方法接收两个参数,then中的参数皆为可选参数,如果onFulfilled或则说onRejected不是一个函数,那么将会被忽略。
  • 如果onFulfilled是一个函数,它必须在promise状态转换为fulfilled时候就被调用,并且promise被赋予的value会成为这个函数(onFulfilled)的第一个参数。onFulfilled不能在promise状态转化为fulfilled前就调用onFulfilled函数不能重复调用
  • 如果onRejected是一个函数,它必须在promise状态转换为rejected时候就被调用,并且promise被赋予的reason会成为这个函数(onRejected)的第一个参数。
    onRejected不能在promise状态转化为rejected前就调用
    onRejected函数不能重复调用
  • 当一个promise转化为fulfilled状态,所有onFulfilled callback会按照回调函数通过then添加时的顺序而执行。当一个promise转化为rejected状态,所有onRejected callback会按照回调函数通过then添加时的顺序而执行。
  • 如果onFulfilled或onRejected回调函数中返回了一个值,假定为x,那么调用一个 promise解析方法。
    如果onFulfilled或者onRejected抛出了一个 exception(异常) e , promise2 必须以这个e作为reason来拒绝promise,使其状态改变为rejected。
    如果onFulfilled不是一个函数且 promise1 的状态为fulfilled,promise2必须以 promise1 的值来fulfilled。
    如果onRejected不是一个函数且 promise1 的状态为rejected,promise2必须以 promise1 的理由来rejected。

3.promise的中度实现

function Promise(){
    ...
    function resolve(value){ 
    setTimeout(function(){ 
      if(self.status === PENDING){
        self.status = FULFILLED; 
        self.value = value; 
        self.onFulfilledCallbacks.forEach(cb => cb(self.value)); 
      }
    })
  }
  function reject(reason){
    setTimeout(function(){
      if(self.status === PENDING){ 
        self.status = REJECTED;
        self.value = reason;
        self.onRejectedCallbacks.forEach(cb => cb(self.value));
      }
    })
  }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
  onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};

  let self = this,
    promise2;

  if(self.status === PENDING){
  
    return promise2 = new Promise(function(resolve,reject){
      self.onFulfilledCallbacks.push((value)=>{
        try{
          let x = onFulfilled(value);
          resolvePromise(promise2,x,resolve,reject);
        }catch (e){
          reject(e); 
        }
      });
      self.onRejectedCallbacks.push((reason)=>{
        try{
          let x= onRejected(reason);
          resolvePromise(promise2,x,resolve,reject);
        }catch (e){
          reject(e);
        }
      });
    });
  }
};

3.1 Promise状态解析方法(promise resolution procedure)

let x= onRejected(reason);resolvePromise(promise2,x,resolve,reject);

  • Promise状态解析方法的作用是将then时返回的promise2的状态改变并赋予其vlaue/reason。
  • 如果 x 是一个thenable,那么该方法将试图将以 x 的状态来改变 promise2 的状态否则就将 promise2 改成 fulfilled 状态,并且value即为 x 的值

3.1.1. 如果 promise2 和 x 是引用关系,则抛出一个 TypeError 做为理由来 reject promise2。
3.1.2. 如果 x 是一个promise ,让promise2采用它的状态。

如果 x 处于pending,promise2 必须保持pending直到 x 转换为 fulfilled或则rejected。
如果 x 是 fulfilled状态,让promise2也为fulfilled,并且让promise2的value为x的value。
如果 x 是 rejected状态,让promise2也为rejected,并且让promise2的value为x的reason。

3.1.3. 如果 x 是一个对象或则函数

Let then be x.then
如果检索 x.then 时候抛出了一个异常e,那么以这个 e来 rejecte promise2。
如果 then 是一个函数,用x作为this,resolvePromise作为第一个参数,rejectPromise作为第二个参数来 call它。

如果resolvePromise被调用,循环调用 promise状态解析方法(原本的x替换为调用resolvePromise传入的参数,假定为y)。
如果rejectPromise被调用,则reject Promise2,reason为调用rejectPromise传入的参数
如果resolvePromise 和 rejectPromise 同时被调用或则多次调用,那么第一个调用的拥有优先权,其它的会被忽略。
如果调用 then 的时候抛出了一个异常 e

如果 resolvePromise 或 rejectPromise 已经被调用,则忽略它。
否则,则用这个e来 reject promise2。

3.1.4如果then不是一个函数,则用x来fulfilledpromise2

Promise.catch的基本实现

promise.prototype.catch = function(onRejected){
    this.then(null,onRejected);
}

Promise.all的基本实现

Promise.all = function(promises){
    return new Promise((resolve,reject)=>{
        let result = [],
            count = 0;

        function done(i,data){
            result[i] = data;
            if(++count===promises.length){
                resolve(result);
            }
        }
        for(let i=0;i<promises.length;++i){
            promises[i].then((value)=>{
                done(i,value);
            },(reason)=>{
                reject(reason);
            });
        }
    });
}

Promise.promisify的基本实现

Promise.promisify = function(fn){
    return function(...args){
        return new Promise((resolve,reject)=>{
            fn.apply(null,[...args,function(err,data){
                err?reject(err):resolve(data);
            }]);
        });
    }
}

Promise.promisifyAll的基本实现

Promise.promisifyAll = function(obj){
    for(var attr in obj){
        if(obj.hasOwnProperty(key)&&typeof obj[attr]==='function'){
            obj[attr+'Async'] = Promise.promisify(obj[attr]);
        }
    }
}

Promise.race的基本实现

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

推荐阅读更多精彩内容