用 class 寫(xiě)法完整實(shí)現(xiàn)一個(gè) Promise

1.前言

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

你可以點(diǎn)我看源碼、點(diǎn)我看原文地址

2.Promise 特征分析

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

3.Promise 的實(shí)現(xiàn)

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

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

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

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

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

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


const PENDDING = 'pendding';// 等待狀態(tài)
const FULFILLED = 'resolved';// 成功操作狀態(tài)
const REJECTED = 'rejected';// 捕獲錯(cuò)誤狀態(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)建一個(gè)未完成狀態(tài)的Promise

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

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

    代碼思路:

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

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

    3. resolve 方法下處理的問(wèn)題是:

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

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

      3、將 [[PromiseValue]] 賦值為方法傳遞進(jìn)來(lái)的參數(shù);

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

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

    5. then 方法:

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

      2、 加入成功操作方法隊(duì)列;

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

    6. catch 方法不贅述

    ps: 注①因?yàn)闊o(wú)法將任務(wù)插入 microtask 中,就用 eventloop結(jié)束作為替代;

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

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

    // ...

    // 清空等待隊(duì)列
    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);
    }
  }

  // 測(cè)試方法


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

  const test2 = new MyPromise((resolve,reject)=>{
    setTimeout(()=>{
      reject('我出錯(cuò)啦!')
    }, 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)建一個(gè)完成狀態(tài)的Promise

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

    分析一波:

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

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

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

    4. 因?yàn)閷?shí)例化的對(duì)象不能獲取寄幾的 static 方法

    5. 通過(guò) try+catch 捕獲 handler 異常,并通過(guò) reject 進(jìn)行拋出;


  // ...
  // construct 方法新增一個(gè)類型,當(dāng) new 關(guān)鍵字進(jìn)來(lái)傳遞的不是一個(gè)函數(shù),咱們同樣在 eventLoop 結(jié)束拋出一個(gè)錯(cuò)誤
  if(Object.prototype.toString.call(handler) !== "[object Function]"){
    eventLoopEndRun(()=>{
      throw new Error(`MyPromise resolver ${typeof handler} is not a function`)
    })
  } else {
    // 方法傳遞,this指向會(huì)變,通過(guò) bind 保持兩個(gè)方法對(duì)當(dāng)前對(duì)象的引用
    // 當(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)});
  }

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

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

  var rejectPromise =  MyPromise.reject('這個(gè)錯(cuò)了');

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

  new MyPromise();

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

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

    全局錯(cuò)誤監(jiān)聽(tīng),監(jiān)聽(tīng)分為兩種(書(shū)上的說(shuō)法是): 一個(gè)觸發(fā)是當(dāng)前事件循環(huán)結(jié)束前沒(méi)有catch 當(dāng)前錯(cuò)誤 Promise --- unhandledRejection;一個(gè)觸發(fā)是當(dāng)前事件循環(huán)后,當(dāng) Promise 被拒絕,并且沒(méi)有 catch 程序,就會(huì)被觸發(fā) --- rejectionHandled。經(jīng)過(guò) node 環(huán)境下測(cè)試(在 Chrome 控制臺(tái)測(cè)試好像無(wú)論如何都不會(huì)被觸發(fā))感覺(jué)是 rejectionHandled 觸發(fā)實(shí)在新的時(shí)間循環(huán)添加 catch 程序后才會(huì)被觸發(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 階段進(jìn)行訂閱 unhanlderReject 事件;

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

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

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

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

于是乎代碼如下:

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

  class PromiseSubscribePublish{

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

    // 監(jiān)聽(tīng)事件綁定
    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)聽(tīng)函數(shù)


      
      if(type === UNHANDLEDREJECTION){
        // 沒(méi)有訂閱事件的 promise 則啥也不干
        if (!this.subscribeUnhandler.size) return;
        // 根據(jù)當(dāng)前事件類型,選擇處理函數(shù)
        changgeStateFuc = (err, promise)=>{
          this.subscribeHandler.set(promise);
          this.subscribeUnhandler.delete(promise, err);
        }
        // 不論如何當(dāng)前時(shí)間循環(huán)下的等待隊(duì)列狀態(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 沒(méi)有進(jìn)行訂閱
        if(!this.subscribeHandler.has(promise)) return;
        // 哪個(gè) promise 發(fā)布 catch 函數(shù),就根據(jù)當(dāng)前 Promise 執(zhí)行相應(yīng)方法,并將其從 Handler 訂閱者里刪除
        
        errFuc && errFuc(promise);
        this.subscribeHandler.delete(promise);

      } 

    }
  }

  // 定義一些靜態(tài)成員變量 默認(rèn)不可寫(xiě)
  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)先級(jí)最高 , 直接加入隊(duì)列
    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);
  }


  // 測(cè)試代碼

  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 鏈返回值

    看到鏈?zhǔn)剑紫认氲降氖?jquery 調(diào)用。jquery 返回的是 jquery 對(duì)象本體。而 Promise 根據(jù)狀態(tài)判斷:

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

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


  //... 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ù)隊(duì)列原則,執(zhí)行肯定在當(dāng)前 then 后,保證能正確拿到前一個(gè) 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)是實(shí)例內(nèi)部調(diào)用時(shí),不能將當(dāng)前 Promise 從 unhandledReject 隊(duì)列中移除;
    // 否則順著生成鏈依次將 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ù)隊(duì)列原則,執(zhí)行肯定在當(dāng)前 then 后,保證能正確拿到報(bào)錯(cuò)的 Promise 的返回值
        this.catch(()=>{
          nextPromiseData && nextPromiseData.iserror === FUCERROR 
          ? reject(nextPromiseData.err) 
            : resolve(nextPromiseData)
        }, true);
        this.then(data=>resolve(data), true)
      })
      nextPromise.linkPrePromise = this;
      return nextPromise;
    }

  }

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

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

    在分析一波之前,調(diào)整幾個(gè)之前的沒(méi)有考慮到的問(wèn)題:

    1. 將狀態(tài)改變函數(shù)覆蓋操作移至 resolve 和 reject 函數(shù)中。
    2. reject 方法體執(zhí)行全都由是否能改變狀態(tài)決定。
    3. reject 新增一個(gè)參數(shù),表示不訂閱 unhandledReject 事件,因?yàn)?then 方法也會(huì)生成新的 Promise,而 then 鏈前有捕獲異常狀態(tài)的 Promise 會(huì)造成重復(fù)報(bào)錯(cuò),catch 無(wú)所謂,因?yàn)楸旧頃?huì)Promise 鏈隊(duì)列。
  // 開(kāi)頭的 '-' 標(biāo)示移除,'+' 表示新增
  // ... 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);

接下來(lái)開(kāi)始分析一波:

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

  2. 因?yàn)樵蹅兌x的 MyPromise 所以判斷類型應(yīng)該是 MyPromise,如果想要通過(guò) Object.prototype.toString.call 去判斷,咱們需要給咱們的類加一個(gè) tag

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

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

  5. 因?yàn)槭庆o態(tài)方法所以不能用 Symbol.species 構(gòu)建實(shí)例。

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


  static all (promiseArr){
    
    
    
    // 因?yàn)槭庆o態(tài)方法 無(wú)法獲取 this 所以不能使用實(shí)例內(nèi)部方法構(gòu)建方式去構(gòu)建新對(duì)象
    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)存放返回?cái)?shù)組的位置;
      let waitPromiseNum = 0; // 統(tǒng)計(jì)未完成的 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){
    
    // 因?yàn)槭庆o態(tài)方法 無(wú)法獲取 this 所以不能使用實(shí)例內(nèi)部方法構(gòu)建方式去構(gòu)建新對(duì)象
    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();
      }

    })
  }

  // 測(cè)試方法

  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)過(guò)程遇到什么問(wèn)題,歡迎及時(shí)提出。

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

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

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

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