用 class 寫法完整實現(xiàn)一個 Promise

1.前言

本文分析 Promise 特性的了解,完整實現(xiàn)了 Promise 所有功能。沒有參考原生 Promise 的寫法,自己根據(jù)思路一步一步完成以及描述,每個構(gòu)建模塊由:1、Promise 特性描述;2、實現(xiàn)特性的完整思路(分析一波) 3、項目代碼;4、功能測試代碼 幾個部分組成。大致用到的知識有: 1、變量私有化;2、訂閱發(fā)布模式;3、eventloop 理解;4、Promise特性;5、class 特性;6、對象類型的判定... 算了不寫了強行塞這么多我也是夠拼的

你可以點我看源碼、點我看原文地址

2.Promise 特征分析

  • Promise 有三種狀態(tài): pending(執(zhí)行中)、 fulfilled(成功執(zhí)行)、settled(異常捕獲);
  • Promise 可以通過 new 關(guān)鍵字創(chuàng)建一個 未完成的 Promise;
  • Promise 可以直接通過 Promise.resolve 創(chuàng)建一個成功完成的 Promise 對象;
  • Promise 可以直接通過 Promise.reject 創(chuàng)建一個異常狀態(tài)的 Promise 對象;
  • 通過 new 關(guān)鍵字創(chuàng)建的 Promise 方法里如果出現(xiàn)錯誤,會被 Promise 的 reject 捕獲;
  • Promise.resolve / Promise.reject 接收 thenable 對象和 Promise 對象的處理方式;
  • 當(dāng)沒有錯誤處理時的,全局的 Promise 拒絕處理;
  • 串聯(lián) Promise 以及 Promise 鏈返回值;
  • Promise.all Promise.race;

3.Promise 的實現(xiàn)

  • 狀態(tài)碼私有化

    開始之前討論一波 class 私有屬性的實現(xiàn),個人想到的方案如下:

    1.通過閉包,將變量存放在 construct 方法里;弊端,所有的其他的對象方法必須在 construct 內(nèi)定義(NO)。

    2.通過在定義 Promise 的環(huán)境下定義一個 Map,根據(jù)當(dāng)前對象索引去獲取相應(yīng)的私有值;弊端,因為 Map 的 key 是強引用,當(dāng)定義的 Promise 不用時也不會被內(nèi)存回收(NO);

    3.通過在定義 Promise 的環(huán)境下定義一個 WeakMap,根據(jù)當(dāng)前對象索引去獲取相應(yīng)的私有值; 優(yōu)勢,木有以上兩種劣勢(不寫點什么感覺難受);

    說了這么多那么咱們要用第三種方法嗎?NO,原生 [[PromiseState]] 是一個內(nèi)部屬性,不暴露在 Promise 上,但是通過瀏覽器的控制臺可以看到,用第三種方式模仿并不能直觀的在控制臺看到,所以我決定還是不要作為私有變量出現(xiàn),但是把枚舉特性干掉了 假裝他是私有變量 心里好過一點 因此你就能看到下面的代碼;


const PENDDING = 'pendding';// 等待狀態(tài)
const FULFILLED = 'resolved';// 成功操作狀態(tài)
const REJECTED = 'rejected';// 捕獲錯誤狀態(tài)

class MyPromise{
  
  constructor(handler){
    // 數(shù)據(jù)初始化
    this.init();
  }
  
  // 數(shù)據(jù)初始化
  init(){
    Object.defineProperties(this,{
      '[[PromiseState]]': {
        value: PENDDING,
        writable: true,
        enumerable: false
      },
      '[[PromiseValue]]': {
        value: undefined,
        writable: true,
        enumerable: false
      },
      'thenQueue':{
        value: [],
        writable: true,
        enumerable: false
      },
      'catchQueue':{
        value: [],
        writable: true,
        enumerable: false
      }
    })
  }
  // 獲取當(dāng)前狀態(tài)
  getPromiseState (){
    return this['[[PromiseState]]'];
  }
  // 設(shè)置當(dāng)前狀態(tài)
  setPromiseState (state) {
    Object.defineProperty(this, '[[PromiseState]]', {
      value: state,
      writable: false
    })
  }

  // 獲取當(dāng)前值
  getPromiseValue (){
    return this['[[PromiseValue]]'];
  }
  // 設(shè)置當(dāng)前值
  setPromiseValue (val) {
    Object.defineProperty(this, '[[PromiseValue]]', {
      value: val
    })
  }
}

  • 創(chuàng)建一個未完成狀態(tài)的Promise

    函數(shù)調(diào)用過程分析:

    1. 使用者通過 new 關(guān)鍵字傳入一個方法;
    2. 方法有兩個參數(shù) resolvereject 兩個方法
    3. 當(dāng)傳入的方法調(diào)用 resolve 時,狀態(tài)變?yōu)?fulfilled,有且只有接收一次 resolve 里的方法里的值作為 [[PromiseValue]],供該 Promise 對象下的 then 方法使用;
    4. 當(dāng)傳入的方法調(diào)用 reject 時,狀態(tài)變?yōu)?rejected,有且只有接收一次 reject 里的方法里的值作為 [[PromiseValue]],供該 Promise 對象下的 catch 方法使用;

    代碼思路:

    1. 首先傳入的函數(shù)應(yīng)該在 construct 方法里進行調(diào)用;

    2. 因具備一個存放待執(zhí)行成功操作方法的隊列,一個存放捕獲異常方法的隊列。

    3. resolve 方法下處理的問題是:

      1、判斷當(dāng)前狀態(tài)是否是等待狀態(tài),如果不是則啥也不干,如果是走第二步

      2、修改[[PromiseState]]為FULFILLED;

      3、將 [[PromiseValue]] 賦值為方法傳遞進來的參數(shù);

      4、成功操作方法的隊列在 eventloop 結(jié)束后依次調(diào)用然后清空,捕獲異常方法的隊列清空;

    4. reject 方法基本就不贅述啦......

    5. then 方法:

      1、 判斷當(dāng)前狀態(tài)是否為等待,是等待進行第 2 步,否則進行第 3 步;

      2、 加入成功操作方法隊列;

      3、 當(dāng)前eventloop 結(jié)束異步調(diào)用;

    6. catch 方法不贅述

    ps: 注①因為無法將任務(wù)插入 microtask 中,就用 eventloop結(jié)束作為替代;

  // 事件循環(huán)最后執(zhí)行
  const eventLoopEndRun = function (handler){
    setImmediate(()=>{
      handler()
    })
  }
  // ...

  class MyPromise{
  
    constructor(handler){
      // ...
      
      // 方法傳遞,通過 bind 保持兩個方法對當(dāng)前對象的引用
      handler(this.resolve.bind(this), this.reject.bind(this));
    }

    // ...

    // 清空等待隊列
    clearQueue (currentState) {
      
      const doQueue = currentState === REJECTED ? this.catchQueue : this.thenQueue;
      const promiseData = this.getPromiseValue();

      doQueue.forEach(queueHandler=>queueHandler(promiseData));
      this.catchQueue = [];
      this.thenQueue = []
    }

    // 狀態(tài)改變方法
    changeStateHandler (currentState, data){

      this.setPromiseState(currentState);
      this.setPromiseValue(data);
      setImmediate(()=>{this.clearQueue(currentState)});
      
      // 保持狀態(tài)只能改變一次
      this.changeStateHandler = null;
      this.setPromiseState = null;
      this.setPromiseValue = null;
    }

    // 不解釋
    resolve (data) {
      this.changeStateHandler && this.changeStateHandler(FULFILLED, data);
    }
    // 不解釋
    reject (err) {
      this.changeStateHandler && this.changeStateHandler(REJECTED, err);
    }

    // 不解釋
    then(thenHandler){
      
      const currentState = this.getPromiseState();
      const promiseData = this.getPromiseValue();

      if (currentState === FULFILLED) thenHandler(promiseData);
      else if (currentState === PENDDING) this.thenQueue.push(thenHandler);
    }

    // 不解釋
    catch(catchHandler){
      
      const currentState = this.getPromiseState();
      const promiseData = this.getPromiseValue();

      if (currentState === REJECTED) catchHandler(promiseData);
      else if (currentState === PENDDING) this.catchQueue.push(catchHandler);
    }
  }

  // 測試方法


  const test1 = new MyPromise((resolve,reject)=>{
    setTimeout(()=>{
      resolve('2s 后輸出了我');
    }, 2000)
  });

  const test2 = new MyPromise((resolve,reject)=>{
    setTimeout(()=>{
      reject('我出錯啦!')
    }, 2000)
  })

  test1.then(data=>console.log(data));
  test1.catch(err=>console.log(err));
  test2.then(data=>console.log(data));
  test2.catch(err=>console.log(err));
  console.log("我是最早的");

  • 創(chuàng)建一個完成狀態(tài)的Promise

    通過 Promise.resolve() 創(chuàng)建一個成功操作的 Promise 對象; Promise.reject() 創(chuàng)建一個捕獲錯誤的 Promise 對象,new 關(guān)鍵字傳入的方法體有報錯,會直接被 reject 捕獲;

    分析一波:

    1. 能直接調(diào)用的方法,妥妥應(yīng)該的是一個靜態(tài)方法;

    2. 調(diào)用之后要生成一個新的 Promise 對象;

    3. 所以咱們就要分兩步走 1,創(chuàng)建一個 Promise 對象,然后調(diào)用其 resolve 方法.

    4. 因為實例化的對象不能獲取寄幾的 static 方法

    5. 通過 try+catch 捕獲 handler 異常,并通過 reject 進行拋出;


  // ...
  // construct 方法新增一個類型,當(dāng) new 關(guān)鍵字進來傳遞的不是一個函數(shù),咱們同樣在 eventLoop 結(jié)束拋出一個錯誤
  if(Object.prototype.toString.call(handler) !== "[object Function]"){
    eventLoopEndRun(()=>{
      throw new Error(`MyPromise resolver ${typeof handler} is not a function`)
    })
  } else {
    // 方法傳遞,this指向會變,通過 bind 保持兩個方法對當(dāng)前對象的引用
    // 當(dāng)然也可以這么玩:data=>this.resolve(data)
    try{
      handler(this.resolve.bind(this), this.reject.bind(this));
    } catch(err) {
      this.reject(err);
    }
  }

  // ...
  // 不解釋
  static resolve (data) {
    return new MyPromise(resolve=>resolve(data));
  }
  // 不解釋
  static reject (err) {
    return new MyPromise((resolve, reject)=>{reject(err)});
  }

  // 測試方法
  var resolvePromise =  MyPromise.resolve(111);

  resolvePromise.then(data=>console.log(data));

  var rejectPromise =  MyPromise.reject('這個錯了');

  rejectPromise.catch(data=>console.log(data));

  new MyPromise();

  var errPromise = new MyPromise(()=>{throw new Error("我錯了")});
  errPromise.catch(data=>console.log(data.message));
  • thenable 對象 + 全局錯誤監(jiān)聽

    thenable 對象是啥?就是有個屬性為 then 方法的對象,then 方法里有兩個參數(shù),resolve、reject 至于 resolve 和 reject 的作用,就不贅述啦 好像還是打了很多字。

    全局錯誤監(jiān)聽,監(jiān)聽分為兩種(書上的說法是): 一個觸發(fā)是當(dāng)前事件循環(huán)結(jié)束前沒有catch 當(dāng)前錯誤 Promise --- unhandledRejection;一個觸發(fā)是當(dāng)前事件循環(huán)后,當(dāng) Promise 被拒絕,并且沒有 catch 程序,就會被觸發(fā) --- rejectionHandled。經(jīng)過 node 環(huán)境下測試(在 Chrome 控制臺測試好像無論如何都不會被觸發(fā))感覺是 rejectionHandled 觸發(fā)實在新的時間循環(huán)添加 catch 程序后才會被觸發(fā),大致流程圖如下。

    流程圖
    
    let rejected;
    
    process.on('unhandledRejection',function(event){
      console.log('onunhandledrejection');
    })
    
    process.on('rejectionHandled',function(event){
      console.log('onrejectionhandled');
    })
    
    rejected = Promise.reject(new Error('xx'))
    
    eventLoopEndRun(()=>{
      console.log(123);
      rejected.catch(err=>{
        console.log(err.message)
      })
      rejected.catch(err=>{
        console.log(err.message)
      })
    }) 
    
    

    分析一波:

    1. 在 reject 階段進行訂閱 unhanlderReject 事件;

    2. catch 函數(shù)中移除當(dāng)前 PromiseunhandledRejection 事件的訂閱,執(zhí)行傳入 catch 前發(fā)布當(dāng)前 PromiserejectionHandled 事件。

    3. 當(dāng)前事件循環(huán)結(jié)束,我們需要優(yōu)先對 unhanlderReject 事件進行發(fā)布,所以我們需要調(diào)整eventLoopEndRun 函數(shù);當(dāng)Promise沒有 catch 程序,且沒有全局沒有 unhanlderReject 監(jiān)聽,我們就要拋出相應(yīng)的錯誤。

    4. 我們需要自定義這個 訂閱發(fā)布者,然后能通過當(dāng)前 Promise 使得事件觸發(fā)綁定相應(yīng)的回調(diào)。

    5. 這個發(fā)布訂閱者具有備的功能有: 1、新增監(jiān)聽回調(diào);2、訂閱和取消訂閱;3、相應(yīng)的事件發(fā)布后,將對應(yīng) map 中 Promise 修改狀態(tài)。

于是乎代碼如下:

  // PromiseSubscribePublish.js
  const UNHANDLEDREJECTION = 'UNHANDLEDREJECTION'; // 當(dāng)前事件循環(huán),無 catch 函數(shù)狀態(tài);
  const REJECTIONHANDLED = 'REJECTIONHANDLED'; // 事件循環(huán)后,無 catch 函數(shù)狀態(tài);

  class PromiseSubscribePublish{

    constructor(){
      this.subscribeUnhandler = new Map();
      this.subscribeHandler = new Map();
      this.errFuc = {}
    }

    // 監(jiān)聽事件綁定
    bindLisener (type, cb){
      console.log(type.toUpperCase(), UNHANDLEDREJECTION)
      if(type.toUpperCase() !== UNHANDLEDREJECTION && type.toUpperCase() !== REJECTIONHANDLED) throw Error('type toUpperCase must be UNHANDLEDREJECTION or REJECTIONHANDLED');
      if(Object.prototype.toString.call(cb) !== "[object Function]") throw Error('callback is not function');
      this.errFuc[type.toUpperCase()] = cb;
    }

    subscribe(promise, err){
      // 訂閱一波,以當(dāng)前 Promise 為 key,err 為參數(shù),加入 unhandler map 中
      this.subscribeUnhandler.set(promise, err)
    }

    quitSubscribe(promise){
      this.subscribeUnhandler.delete(promise);
    }

    publish (type, promise) {
      
      let changgeStateFuc; // 定義當(dāng)前狀態(tài)變換操作
      const errFuc = this.errFuc[type]; // 當(dāng)前綁定的監(jiān)聽函數(shù)


      
      if(type === UNHANDLEDREJECTION){
        // 沒有訂閱事件的 promise 則啥也不干
        if (!this.subscribeUnhandler.size) return;
        // 根據(jù)當(dāng)前事件類型,選擇處理函數(shù)
        changgeStateFuc = (err, promise)=>{
          this.subscribeHandler.set(promise);
          this.subscribeUnhandler.delete(promise, err);
        }
        // 不論如何當(dāng)前時間循環(huán)下的等待隊列狀態(tài)全部需要變更
        if(errFuc){
          this.subscribeUnhandler.forEach((err, promise)=>{
            errFuc(err, promise)
            changgeStateFuc(err, promise)
          })
        } else {
          this.subscribeUnhandler.forEach((err, promise)=>{
            changgeStateFuc(err, promise)
          })
          console.error('Uncaught (in promise)', err);
        }

      } else {
        // 如果該 promise 沒有進行訂閱
        if(!this.subscribeHandler.has(promise)) return;
        // 哪個 promise 發(fā)布 catch 函數(shù),就根據(jù)當(dāng)前 Promise 執(zhí)行相應(yīng)方法,并將其從 Handler 訂閱者里刪除
        
        errFuc && errFuc(promise);
        this.subscribeHandler.delete(promise);

      } 

    }
  }

  // 定義一些靜態(tài)成員變量 默認不可寫
  Object.defineProperties(PromiseSubscribePublish, {
    [UNHANDLEDREJECTION]:{
      value: UNHANDLEDREJECTION
    },
    [REJECTIONHANDLED]:{
      value: REJECTIONHANDLED
    }
  })

  module.exports = PromiseSubscribePublish;

  // MyPromise.js
  // ..
  const PromiseSubscribePublish = require('./PromiseSubscribePublish');

  const promiseSubscribePublish = new PromiseSubscribePublish();

  // 事件循環(huán)最后執(zhí)行
  const eventLoopEndRun = (()=>{
    let unhandledPub;
    let timer;
    const queueHandler = [];
    // 激活事件循環(huán)最后執(zhí)行
    const activateRun = ()=>{
      // 截流
      timer && clearTimeout(timer);
      timer = setTimeout(()=>{
        unhandledPub && unhandledPub();
        let handler = queueHandler.shift();
        while(handler){
          handler();
          handler = queueHandler.shift();
        }
      },0);
    }
    
    // 設(shè)置 unhanldedReject 優(yōu)先級最高 , 直接加入隊列
    return (handler,immediate)=> {
      immediate ? unhandledPub = handler : queueHandler.push(handler);
      activateRun();
    }
  })()
  
  //...
  reject (err) {
    this.changeStateHandler && this.changeStateHandler(REJECTED, err);
    promiseSubscribePublish.subscribe(this, err);
    // 存在 reject ,事件循環(huán)結(jié)束發(fā)布 UNHANDLEDREJECTION
    eventLoopEndRun(()=>
      promiseSubscribePublish.publish(PromiseSubscribePublish.UNHANDLEDREJECTION, this),
      true
    );
  }

  //...

  static unhandledRejectionLisener(cb){
    promiseSubscribePublish.bindLisener(PromiseSubscribePublish.UNHANDLEDREJECTION ,cb)
  }

  static rejectionHandledLisener(cb){
    promiseSubscribePublish.bindLisener(PromiseSubscribePublish.REJECTIONHANDLED ,cb)
  }

  // ...
  catch(catchHandler){
    
    const currentState = this.getPromiseState();
    const promiseData = this.getPromiseValue();

    // 取消當(dāng)前事件循環(huán)下 reject 狀態(tài)未 catch 事件訂閱;
    promiseSubscribePublish.quitSubscribe(this);
    
    if (currentState === REJECTED) {
      
      eventLoopEndRun(()=>{
        // 發(fā)布 catch 處理
        promiseSubscribePublish.publish(PromiseSubscribePublish.REJECTIONHANDLED, this);
        catchHandler(promiseData);
      });

    }
    else if (currentState === PENDDING) this.catchQueue.push(catchHandler);
  }


  // 測試代碼

  MyPromise.unhandledRejectionLisener((err,promise)=>{
    console.log(err, promise);
  }) 
  MyPromise.rejectionHandledLisener((err,promise)=>{
    console.log(err, promise);
  }) 
  var myPromise = MyPromise.reject(11);
  // myPromise.catch(()=>{console.log('catch')});
  setTimeout(()=>{
    myPromise.catch(()=>{console.log('catch')});
  },1000)


  • 串聯(lián) Promise 以及 Promise 鏈返回值

    看到鏈式,首先想到的是 jquery 調(diào)用。jquery 返回的是 jquery 對象本體。而 Promise 根據(jù)狀態(tài)判斷:

    • 當(dāng)是操作成功狀態(tài)時,調(diào)用 catch 會返回和當(dāng)前 Promise[[PromiseStatus]][[PromiseValues]] 狀態(tài)相同新構(gòu)建的 Promise;調(diào)用 then 方法時,返回和當(dāng)前 Promise[[PromiseStatus]] 相同的,[[PromiseValues]] 值為 then 方法返回值的 新構(gòu)建的 Promise;
    • 當(dāng)是捕獲錯誤狀態(tài)時,調(diào)用 then 會返回和當(dāng)前 Promise[[PromiseStatus]][[PromiseValues]] 狀態(tài)相同新構(gòu)建的 Promise;調(diào)用 catch 方法時, 返回操作成功的新構(gòu)建的 Promise ,[[PromiseValues]] 值為 catch 方法返回值;
    • 當(dāng)執(zhí)行 catch 或 then 方法體內(nèi)有報錯,直接返回一個新構(gòu)建捕獲錯誤的 Promise ,[[PromiseValues]] 為那個錯誤;
    • 如果 Promise 中有一環(huán)出現(xiàn)錯誤,而鏈中沒有 catch 方法,則拋出錯誤,否則把鏈上的所有 Promise 都從 unhandledRejuect 訂閱中去除。
    • 因為 then 和 catch 回調(diào)方法是當(dāng)前事件循環(huán)結(jié)束時才執(zhí)行,而 catch 去除 Promise 鏈上 unhandledRejuect 訂閱是當(dāng)前事件循環(huán),如果鏈上有方法報錯,unhandledRejuect 訂閱會再次發(fā)生,這樣會造成哪怕當(dāng)前報錯 Promise 后有 catch,也會拋出錯誤,因此需要給當(dāng)前 Promise 加一個屬性,以標志鏈后有 catch,使得其不訂閱 unhandledRejuect 事件。

分析一波:
1. 要在實例方法中,創(chuàng)建另一個當(dāng)前類的實例時,必須用到當(dāng)前類的構(gòu)造函數(shù)。當(dāng)咱們的類被繼承出一個派生類,咱們希望返回的是那個派生類,于是不能直接 new MyPromise 去創(chuàng)建,而要使用一個 Symbol.species
2. 新建 Promise 和之前的 Promise 存在關(guān)聯(lián),所以當(dāng)前 Promise 的狀態(tài)決定新 Promise 狀態(tài),構(gòu)建新 Promise 的過程中當(dāng)前 Promise 的捕獲函數(shù)不能將其訂閱從 unhandledReject 中移除,所以需要一個標志位來標識 then 函數(shù)屬性。
3. Promise 鏈上如果出現(xiàn) catch 函數(shù),?鏈上 catch 函數(shù)之前的所有 Promise 都將從訂閱 unhandledReject Map 中移除,因此 Promise 需要記錄鏈上的上一級 Promise;
4. Promise then 或 catch 方法體內(nèi)報錯將構(gòu)建一個捕獲錯誤狀態(tài)的 Promise,因此需要一個函數(shù)去捕獲可能發(fā)生的錯誤;


  //... MyPromise.js


  const runFucMaybeError = handler => {
    try {
      return handler();
    } catch(err) {
      return {
        iserror: FUCERROR,
        err
      };
    }
  }

  const clearLinksSubscribe = linkPrePromise=>{
    while(linkPrePromise && !linkPrePromise.hascatch){
      linkPrePromise.hascatch = true;
      promiseSubscribePublish.quitSubscribe(linkPrePromise);
      linkPrePromise = linkPrePromise.linkPrePromise;
    }
  }
  // 不解釋
  then(thenHandler, quitReturn){
    
    const currentState = this.getPromiseState();
    const promiseData = this.getPromiseValue();
    let nextPromiseData;
    if (currentState === FULFILLED) eventLoopEndRun(()=>{
      nextPromiseData = runFucMaybeError(()=>thenHandler(promiseData))
    });
    else if (currentState === PENDDING) this.thenQueue.push(data=>{
      nextPromiseData = runFucMaybeError(()=>thenHandler(data))
    });

    if(!quitReturn){
      const nextPromise = new this.constructor[Symbol.species]((resolve,reject)=>{
        
        this.catch(err=>{
          reject(err);
        }, true);
        // 根據(jù)隊列原則,執(zhí)行肯定在當(dāng)前 then 后,保證能正確拿到前一個 Promise 的返回值
        this.then(()=>{
          nextPromiseData && nextPromiseData.iserror === FUCERROR 
            ? reject(nextPromiseData.err) 
              : resolve(nextPromiseData)
        }, true)
      })
      nextPromise.linkPrePromise = this;
      return nextPromise;
    };

  }

  catch(catchHandler, quitReturn){
    
    const currentState = this.getPromiseState();
    const promiseData = this.getPromiseValue();
    let nextPromiseData;
    // 取消當(dāng)前事件循環(huán)下 reject 狀態(tài)未 catch 事件訂閱;
    // 當(dāng)是實例內(nèi)部調(diào)用時,不能將當(dāng)前 Promise 從 unhandledReject 隊列中移除;
    // 否則順著生成鏈依次將 Promise 移除;
    if(!quitReturn)clearLinksSubscribe(this)
    if (currentState === REJECTED) {
      
      eventLoopEndRun(()=>{
        // 發(fā)布 catch 處理
        promiseSubscribePublish.publish(PromiseSubscribePublish.REJECTIONHANDLED, this);
        nextPromiseData = runFucMaybeError(()=>catchHandler(promiseData));
      });

    }
    else if (currentState === PENDDING) this.catchQueue.push(data=>{
      nextPromiseData = runFucMaybeError(()=>{catchHandler(data)})
    });

    if(!quitReturn){
      
      const nextPromise = new this.constructor[Symbol.species]((resolve,reject)=>{
        // 根據(jù)隊列原則,執(zhí)行肯定在當(dāng)前 then 后,保證能正確拿到報錯的 Promise 的返回值
        this.catch(()=>{
          nextPromiseData && nextPromiseData.iserror === FUCERROR 
          ? reject(nextPromiseData.err) 
            : resolve(nextPromiseData)
        }, true);
        this.then(data=>resolve(data), true)
      })
      nextPromise.linkPrePromise = this;
      return nextPromise;
    }

  }

  // 測試代碼
  const test1 = new MyPromise((resolve,reject)=>{
    setTimeout(()=>{
      resolve('2s 后輸出了我');
    }, 2000)
  });


  test1.then(data=>{
    console.log(data);
    return '你好'
  }).then(data=>{
    console.log(data);
    return '不好'
  }).then(data=>{
    console.log(data);
  });

  test1.catch(err=>console.log(err)).then(data=>{
    console.log(data);
    return 'gggg'
  }).then(data=>{
    console.log(data);
  });

  const test2 = new MyPromise((resolve,reject)=>{
    throw new Error('xx');
  })

  test2.then(data=>console.log(data)).catch(err=>console.log(err));

  test2.catch(err=>console.log(err)).then(data=>{
    console.log(data);
    return '你好'
  }).then(data=>{
    console.log(data);
    return '不好'
  }).then(data=>{
    console.log(data);
  });
  var a = MyPromise.resolve(1);
  var b = a.then(data=>{throw new Error('11')}).catch(err=>{console.log(err.message)})
  • Promise.all + Promise.race;

    Promise.all 有如下特性: 1、接收一個具有[Symbol.iterator]函數(shù)的數(shù)據(jù), 返回一個 Promise,該 Promise 成功操作,then 方法傳入一個數(shù)組,數(shù)組數(shù)據(jù)位置和迭代器迭代返回的順序相關(guān)聯(lián),該 Promise 捕獲錯誤 catch 里的傳入捕獲的錯誤; 2、 迭代器遍歷結(jié)果如果是 Promise , 則將其 PromiseValue 作為值,插入傳入數(shù)組對應(yīng)的位置,當(dāng)遍歷結(jié)果不是 Promise 直接插入數(shù)組對應(yīng)位置,當(dāng)遇到捕獲錯誤,或者 Promise 出現(xiàn)錯誤時直接將狀態(tài)轉(zhuǎn)變?yōu)?rejected 狀態(tài) ,從 catch 拿到相應(yīng)錯誤的值;總結(jié)就是有錯馬上拋,要不等所有數(shù)據(jù)處理完才改變狀態(tài);

    Promise.race 就不贅述:記住幾點,傳入?yún)?shù)要求和 .all 相同,數(shù)據(jù)處理方式是,先到先得,率先處理完的數(shù)據(jù)直接修改狀態(tài)。

    在分析一波之前,調(diào)整幾個之前的沒有考慮到的問題:

    1. 將狀態(tài)改變函數(shù)覆蓋操作移至 resolve 和 reject 函數(shù)中。
    2. reject 方法體執(zhí)行全都由是否能改變狀態(tài)決定。
    3. reject 新增一個參數(shù),表示不訂閱 unhandledReject 事件,因為 then 方法也會生成新的 Promise,而 then 鏈前有捕獲異常狀態(tài)的 Promise 會造成重復(fù)報錯,catch 無所謂,因為本身會Promise 鏈隊列。
  // 開頭的 '-' 標示移除,'+' 表示新增
  // ... changeStateHandler 方法
  -  this.changeStateHandler = null;

  resolve (data) {
    if(this.changeStateHandler){
      this.changeStateHandler(FULFILLED, data);
      // 保持狀態(tài)只能改變一次
      this.changeStateHandler = null;
    }
  }

  reject (err, noSubscribe) {
    if(this.changeStateHandler){ 
      this.changeStateHandler(REJECTED, err);
      !noSubscribe && !this.hascatch && promiseSubscribePublish.subscribe(this, err);
      // 存在 reject ,事件循環(huán)結(jié)束發(fā)布 UNHANDLEDREJECTION
      eventLoopEndRun(()=>
        promiseSubscribePublish.publish(PromiseSubscribePublish.UNHANDLEDREJECTION, this),
        true
      );
      // 保持狀態(tài)只能改變一次
      this.changeStateHandler = null;
    }
  }

  // then 方法
  - this.catch(err=>{
    reject(err)
  }, true);
  
  + this.catch(err=>reject(err, true), true);

接下來開始分析一波:

  1. 首先咱們的判斷,傳入的是否具有 Symbol.iterator,沒有就直接拋錯(Promise 狀態(tài)會直接變?yōu)?reject,就不往下說了);

  2. 因為咱們定義的 MyPromise 所以判斷類型應(yīng)該是 MyPromise,如果想要通過 Object.prototype.toString.call 去判斷,咱們需要給咱們的類加一個 tag

  3. .all 處理完一波數(shù)據(jù)插入結(jié)果值對應(yīng)的位置,判斷是否數(shù)據(jù)完全處理完,如果全部處理完才改變狀態(tài)。.race 處理完那個直接改變狀態(tài),忽略后面、忽略后面、忽略后面(重要的事情嗶嗶3次)。

  4. 兩邊如果有傳入的 Promise 狀態(tài)出現(xiàn)捕獲異常,返回的 Promise 狀態(tài)即變?yōu)楫惓#琧atch 得到的值即為傳入 Promise 異常的那個異常 繞死你。

  5. 因為是靜態(tài)方法所以不能用 Symbol.species 構(gòu)建實例。

  // MyPromise.js 最后頭
  MyPromise.prototype[Symbol.toStringTag] = "MyPromise";


  static all (promiseArr){
    
    
    
    // 因為是靜態(tài)方法 無法獲取 this 所以不能使用實例內(nèi)部方法構(gòu)建方式去構(gòu)建新對象
    return new MyPromise((resolve,reject)=>{
      const iterator = isIterator(promiseArr);
      
      if(typeof iterator === 'string'){
        console.error(iterator);
        throw new Error(iterator);
      }

      let data = iterator.next();
      const result = [];
      let index = -1; // Promise 應(yīng)存放返回數(shù)組的位置;
      let waitPromiseNum = 0; // 統(tǒng)計未完成的 Promise;
      
      let checkAllEnd = () => {
        return waitPromiseNum === 0;
      }

      while (data) {
        if(data.done) break;
        index ++;
        if(Object.prototype.toString.call(data.value) !== "[object MyPromise]"){
          result[index] = data.value;
        } else {

          (index=>{
            const promise = data.value; 
            waitPromiseNum++;
            promise.then(data=>{
              result[index] = data;
              waitPromiseNum--;
              // 看是否 Promise 全部完成
              if(checkAllEnd())resolve(result);
            }).catch(data=>reject(data));
          })(index)

        }
        data = iterator.next();
      }

      if(checkAllEnd())resolve(result);
    })
  }

  static race (promiseArr){
    
    // 因為是靜態(tài)方法 無法獲取 this 所以不能使用實例內(nèi)部方法構(gòu)建方式去構(gòu)建新對象
    return new MyPromise((resolve,reject)=>{
      const iterator = isIterator(promiseArr);

      if(typeof iterator === 'string'){
        console.error(iterator);
        throw new Error(iterator);
      }

      let data = iterator.next();
      while (data) {
        if(data.done) break;
        if(Object.prototype.toString.call(data.value) !== "[object MyPromise]"){
          return resolve(data.value);
        } else {
          data.value
            .then(data=>resolve(data))
            .catch(data=>reject(data));
        }
        data = iterator.next();
      }

    })
  }

  // 測試方法

  MyPromise.all(
    [
      MyPromise.resolve(1),
      new MyPromise(resolve=>setTimeout(()=>resolve(2), 1000)),
      MyPromise.resolve(3)
    ]).then(data=>{console.log(data)});

  MyPromise.all([
    1,
    new MyPromise(resolve=>setTimeout(()=>resolve(2), 1000)),
    MyPromise.resolve(3)
    ]).then(data=>{console.log(data)});

  MyPromise.all([
    MyPromise.resolve(1),
    new MyPromise(resolve=>setTimeout(()=>resolve(2), 1000)),
    MyPromise.reject(3)
    ]).then(data=>{console.log(data)});


  MyPromise.race([
    MyPromise.resolve(1),
    new MyPromise(resolve=>setTimeout(()=>resolve(2), 1000)),
    MyPromise.resolve(3)
    ]).then(data=>{console.log(data)});

  MyPromise.race([
    1,
    new MyPromise(resolve=>setTimeout(()=>resolve(2), 1000)),
    MyPromise.resolve(3)
    ]).then(data=>{console.log(data)});
    
  MyPromise.race([
    MyPromise.resolve(1),
    new MyPromise(resolve=>setTimeout(()=>resolve(2), 1000)),
    MyPromise.reject(3)
    ]).then(data=>{console.log(data)});

結(jié)束

如果發(fā)現(xiàn)過程遇到什么問題,歡迎及時提出。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Promise 對象 Promise 的含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,823評論 1 56
  • 本文適用的讀者 本文寫給有一定Promise使用經(jīng)驗的人,如果你還沒有使用過Promise,這篇文章可能不適合你,...
    HZ充電大喵閱讀 7,444評論 6 19
  • 目錄:Promise 的含義基本用法Promise.prototype.then()Promise.prototy...
    BluesCurry閱讀 1,557評論 0 8
  • Promiese 簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果,語法上說,Pr...
    雨飛飛雨閱讀 3,484評論 0 19
  • 張行烜,是張家年輕一輩里目前武術(shù)最高的一個,其余的都在三流初期,中期那,很多甚至都到不了三流高手。 不過張家也不...
    白速閱讀 635評論 0 3

友情鏈接更多精彩內(nèi)容