手寫簡單promise和斷點式分析

簡單 promise 的實現

看過不少種 promise 的實現,一直想本身寫一個,吭哧了好久,終於寫出來了(先不寫 all 等方法和 reject 方法,後期在 issues 中補充),就拿它做爲第一篇博客吧promise

大綱

  1. 先羅列一下可能不全的 promiseA+規範
  2. 逆推,從 promise 使用到實現
  3. 實現思路和步驟代碼
  4. 總體代碼夾註釋
  5. 斷點式分析

promiseA+ 規範(從別人文章裏看到的,可能不全)

  1. promise 須要是一個對象或者函數
  2. promise 須要有三個狀態 pending,fulfilled,rejected,狀態變化只能是 pending 到後二者之一,且不可逆
  3. promise 必須有一個 then 方法,該方法接收兩個函數參數 onFunfilled 和 onRejected。前者爲 pending 到 fulfilled 時執行的方法,後者爲 pending 到 rejected 的方法。
  4. then 方法必須返回一個新的 promise
  5. 確保 then 裏面的函數異步執行

promise 的使用

  • 代碼瀏覽器

    let proB = new PromiseB((resolve) => { console.log('promiseB: resolve'); resolve('then1') })
    proB.then(value => {
      console.log('then1', value)
      return 'then2'
    }).then(//建立pro3
    data => {
      return new PromiseB((resolve) => {//是pro4
        setTimeout(() => {
          console.log('非正常then2:', data)
          resolve('then3')
        }, 1000)
      })
    }).then(value => {
      console.log('數據then3', value)
      return 'then4'
    }).then(value => {
      console.log('數據then4', value)
      return 'then5'
    }).then().then(
      value => { console.log('then5:', value) }
    )
    複製代碼
  • 本身實現執行結果: 異步

    selfPromiseResult

  • promise 執行的結果: async

    promiseResult

實現思路

  • 先看一下使用中須要注意的方面函數

    1. promise 的函數參數是當即執行的,參數爲 resolve,由此開啓 promise 邏輯
    2. then 的兩個函數參數有多是同步執行,也有多是異步執行(注:這裏分兩種狀況判斷區分異步同步使用 state==='pending'便可
    3. then 可能沒有參數
    4. then 參數函數中 return 的數據下一個 then 也得能拿到才能夠
  • 具體實現思路ui

    1. 先講一個概念,鏈式執行,我在下面一直說同步異步,其實就是當 onFulfilled 返回是一個 promise 的狀況下,其後面的調用都應該是在該參數 promise 的 resolve 觸發之後才繼續執行,而這個 resolve 通常都是在一個異步環境中觸發,定時器,接口清求等,全部後面的步驟就有了‘等待’這個過程。
    2. 首先建立一個名爲 promiseB 的類接收兩個函數參數,該類有一個 then 方法,有一個 state 屬性,then 方法接收兩個函數參數數 onFunfilled 和 onRejected,then 返回一個新的 promise
    class PromiseB{
      constructor(excutor){
      this.state='pending'// 'pending'|'fulfilled'|'redected'
      excutor(this.resolve)
      }
      then=(onFunfilled,onRejected)=>{//class properties語法,目前谷歌瀏覽器已經支持了
      return new PromiseB(/*這裏應該有一個函數參數*/)
      }
    
    }
    複製代碼
    1. 上面代碼中 classProperties 語法就是就是會自動綁定該方法到實例上,至關於 class A{constructor(){this.handleA=this.handleA.bind(this)};handuleA(){console.log('who am I belong to:',this)}} onFunfilled 函數參數的參數是 resolve 的數據或者其餘 then 的返回值 then 返回的 promise 的參數下面講
    2. 除了上述須要的數據之外,excutor 的參數爲 resolve 方法,還須要 一個 value 變量 來存儲每次 onFulfilled 函數接收的參數值。爲了知足異步調用的狀況,還須要有一個狀態去存儲須要延後執行的 onFulfilled 函數(一開始準備叫 callbacks,後來發現不貼切,在斷點分析中會講緣由),就叫 asyncObj 吧。
    class PromiseB{
      constructor(excutor) {
       this.state='pending'// 'pending'|'fulfilled'|'redected'
       this.value = null
       this.asyncObj = {}
       excutor(this.resolve)
     }
     then=(onFunfilled,onRejected)=>{//class properties語法,目前谷歌瀏覽器已經支持了
       return new PromiseB(/*這裏應該有一個函數參數*/)
     }
     resolve = (newValue) => {}
    }
    複製代碼
    1. 接下來就是實現 then 和 resolve 兩個方法,先講 then 方法,其實主要是講 then 返回的內部 promise 的邏輯。 then 方法執行的時候分爲同步和異步,即該 promise 的 then 觸發的時候,其參數 onfulfilled 的參數 value 可否拿到正確的值,即該 promise resolve 的參數。咱們經過 this.state==='pending'來輔助判斷,this.state 的修改只能在 resolve 中進行,若是 this.pendin==='pending'爲真,那麼認爲是異步的,先在還不能執行 onFulfilled,講其存在該 promise 的 asyncObj 中,若是 this.state='pending'爲假,那麼認爲咱們已經能夠拿到正確的值,爲同步,執行 onFulfilled。同時爲了保證 promise 能夠繼續鏈式調用下去,而且下一個 promise 能夠拿到正確值,咱們須要在異步時把本次 then 返回的新 promise 的 resolve 存起來,同步則把 onFulfilled(this.value)的結果經過新 promise.resolve 傳遞出去,若是該 then 沒有參數,即沒有 onfulfilled,直接 resolve 掉 this.value。至於爲什麼 promise.resolve 能夠把數據傳遞給下一個 promise,咱們在 resolve 和斷點分析中都會講。下面是 then 的實現,將上述描述轉化成代碼:
    then = (onFUlfilled) => {
        return new PromiseB(resolve => {
          if (this.state === "pending") {
            this.asyncObj={ onFUlfilled, resolve };
          } else {
            if(!onFUlfilled)return resolve(this.value)
            resolve(onFUlfilled(this.value));
          }
        });
      };
    複製代碼
    1. 實現 resolve 同步方法,這裏應該會有一行代碼讓你至關迷惑,我也是斷點調試才完全搞清楚的 resolve 是在新建立的 promise 中能夠被使用者顯式調用的,同時它也是在 then 隱式返回的 promise 中隱式調用的。resolve 首先要接收一個參數 value,而後結合上面 then 講到的,在 resolve 中要改變 promise 的狀態。代碼實現(resolve 和 then 同級)
    resolve = newValue => {
        this.state = "fulfilled";
        this.value = newValue;
    };
    複製代碼
    1. 實現 resolve 同步和異步方法,上述代碼執行同步的代碼是沒問題了,可是有一個問題,那就是異步的狀況下怎麼辦?結合面 then 方法,then 方法中若是狀態沒有改變會把 onfullfiled 和新返回的 promise 的 resolve 存到 asyncObj 中,resolve 固然是在正確的時間節點下把他們取出來調用一番啦。至於這個正確的時間節點其實就是當參數 promise 的 resolve 觸發之後下一步執行便可。代碼至於怎麼實現其實就是首先遇見異步或者說 onFulfilled 返回了一個 promise,更確切一點就是當某一個 promise 的 resolve 方法的參數是一個 promise 的時候,第一 不要去修改這個 promise 的 value,由於咱們想要的 value 是參數 promise 調 resolve 返回的值,而不是這個 promise 自己,第二 不要去修改狀態,只要不修改狀態,後續執行的全部的 then 都會將 onFulfilled 和新的 resolve 存儲到該 promise 的 asyncObj 中而不是去執行它,這就實現了等待的功能,以及保留了之後鏈式執行的順序。第三 當時間節點合適的時候取出 asynObj 中的方法,執行他們。第四,也是最重要的一步,若是 resolve 的參數 value 是一個 promise,執行這個參數 promise 的 then 方法,而且將本 promise 的 resolve 做爲 onfulfilled 傳遞進去。上述說的時間節點其實就是 resolve 的參數是否是 promise。講了一堆應該仍是沒有描述清楚,下面的斷點分析會完全講清楚,下面是轉述代碼
    resolve = newValue => {
        if (newValue && newValue.then) {
          newValue.then(this.resolve);
        } else {
          this.state = "fulfilled";
          this.value = newValue;
          if(!asyncObj.onFUlfilled && asyncObj.resolve)return asyncObj.resolve(this.value)
          asyncObj.resolve(asyncObj.onFUlfilled(this.value))
        }
      };
    複製代碼
  • 所有代碼實現:this

    class PromiseB {
      constructor(excutor) {
        this.state = "pending";
        this.value = null;
        this.asyncObj = {};
        excutor(this.resolve);
      }
      then = onFulfilled =>
        new PromiseB(resolve => {
          if (this.state === "pending")
            return (this.asyncObj = { onFulfilled, resolve });
          if (!onFulfilled) return resolve(this.value);
          resolve(onFulfilled(this.value));
        });
      resolve=value=>{
        if(value&&value.then){
          value.then(this.resolve)
        }else{
          this.state='fulfilled'
          this.value=value
          setTimeout(() => {
              const {onFulfilled,resolve} = this.asyncObj
              if(onFulfilled&&resolve)resolve(onFulfilled(this.value))
              if(!onFulfilled&&resolve)resolve(this.value)
            },
          0);
        }
      }
    }
    複製代碼

上面留下幾個坑

  1. resolve 爲何能夠保存鏈式結構而且讓異步之後的代碼執行下去?

    由於在參數不爲 promise 而且 asyncObj 有值的狀況下,看第 23 行代碼,這裏實際上是一個遞歸,onFulfiled 其實就是該 promise 的 then 的參數,resolve 是下個 prommsie 的 resolve。實現了遞歸遍歷數據。spa

  2. 爲何 asyncObj 不叫 callbacks,若是看過其餘實現的話,你會發現,大部分人都會叫 callbacks。

    其實通常來講,若是真的看懂了上面的代碼是不會有這個疑問的,這裏並不算是發佈訂閱, 通常咱們發佈訂閱都是存一系列的 callback,一次 emit,執行全部回調,咱們這裏是遞歸,每次執行的時候只有一個以前存好的 onFulfilled 和一個 resolve調試

  3. 參數 promise 的 onfulfilled 爲何是 this.resolve?

    這個其實得留到斷點分析裏去講,由於篇幅有點長,並且我本身感受也沒有徹底搞清楚,我是逆推分析出來的。code

分析

其實爲了搞懂上面那些東西,我用了差很少三天的時間,最後經過編號和打斷點才搞清楚地。其實就幾個問題,若是都能搞清楚,那你應該對 promise 沒有任何疑問了。

答案在文中,結果在底部

  1. 使用示例中總共有幾個 promise?
  2. 使用示例中總共執行了幾回 then,順序是怎樣的?
  3. 使用示例中總共執行了幾回 resolve,順序是怎樣的?

斷點分析

最好先想一下上面的問題,若是答案不對或者沒有搞很清楚,帶着疑問往下看,收穫會更大。生成的第幾個promise就記作pro幾,例如第一個promise就叫pro1。pro1的onfulfilled就是pro1的then的參數onfulfilled.

  1. 第一個promise爲pro1,執行 excutor(resolve),即執行 console.log('promiseB: resolve’) ,並觸發 pro1.resolve('then1'),步驟1結束
  2. pro1.resolve('then1')觸發, 判斷傳入的值是否爲promise,值'then1'不是promise:pro1.value改變爲pro1.resolve參數'then1',pro1狀態改變,執行setTimeout裏的代碼,由於asyncObj爲空,其實啥都不會執行,步驟2結束。
  3. pro1.resolve代碼塊結束之後,示例代碼第二行 proB.then... 執行,拉開了promise chain的序幕。 pro1.then(onFulfilled) 觸發,返回新的promise pro2 ,判斷pro1的狀態,經過:執行Pro1的onFulfilled-> value=>{console.log('then1', value)return 'then2'},執行完畢之後獲取到返回值'then2',將'then2'做爲pro2.resolve的參數,即執行 pro2.resolve('then2') ,步驟3結束
  4. pro2.resolve('then2')觸發,判斷傳入的值是否爲promise,值'then2'不是promise:pro2.value改變爲pro2.resolve參數‘then2’,pro2狀態改變,執行setTimeout裏的代碼,由於asyncObj爲空,其實啥都不會執行,步驟4結束。
  5. pro2.then(onFulfilled) 觸發,返回新的promise pro3 ,判斷pro2的狀態,經過,執行pro2的then的onfulfilled->data => { return new PromiseB((resolve) => {setTimeout(() => { console.log('非正常then2:', data) resolve('then3') }, 1000) })},建立並執行pro4的excutor(resolve),setTimeout(() => { console.log('非正常then2:', data) resolve('then3') }, 1000) }),這個resolve是pro4的,獲得返回值 一個promise即pro4 ,將返回值做爲value傳遞給pro3的resolve,執行pro3.resolve(pro4),步驟5結束
  6. pro3.resolve(pro4) 觸發,判斷傳入的值是否爲promise,值 pro4 是promise:執行 pro4.then(pro3.resolve),步驟6結束。
  7. pro4.then(pro3.resolve)觸發,返回新的promise pro5,判斷pro4的狀態,由於狀態的改變都是在this.resolve執行且參數不是promise的狀況下才會改變,而pro4.resolve尚未執行,因此 不經過 :將 參數promise即pro4onfulfilled即pro3的resolvepro5的resolve 存起來,存到 參數promise即pro4的asyncObj 中,即此時 pro4.asyncObj={onfulfilled:pro3.resolve,resolve:pro5.resolve}步驟7結束,執行完畢之後繼續回到promise chain,此時應該執行的是 pro3.then(onFulfilled)
  8. pro3.then(onFulfilled)觸發(我以前的思路一直是卡在步驟7結束)。返回新的promise pro6 ,判斷pro3的狀態,由步驟6可知,不經過:將pro3的onFulfilled-> (value => {console.log('數據then3', value)return 'then4'}) 和pro6的resolve存到pro3的asyncObj中,即此時pro3.asyncObj={onfulfilled:pro3.onFulfilled,resolve:pro6.resolve} ,返回pro6,執行 pro6.then(onFulfilled) 步驟8結束。
  9. pro6.then(onFulfilled)觸發,返回新的promise pro7 ,判斷pro6的狀態,和步驟7中pro4對於狀態的判斷一致,不經過:將pro6的onfulfilled->value=>{console.log('數據then4', value)return 'then5'}和pro7的resolve存到pro6的asyncObj中,即此時 pro6.asyncObj={onfulfilled:pro6.onFulfilled,resolve:pro7.resolve},返回pro7,執行pro7.then(onFulfilled),步驟9結束
  10. pro7.then(onFulfilled)觸發,返回新的promise pro8 ,判斷pro7的狀態,和步驟7中pro4對於狀態的判斷一致,不經過:將pro7的onfulfilled->undefined和pro8的resolve存到pro7的asyncObj中,即此時 pro7.asyncObj={onfulfilled:pro7.onFulfilled,resolve:pro8.resolve},返回pro8,執行pro8.then(onFulfilled),步驟10結束
  11. pro8.then(onFulfilled)觸發,返回新的promise pro9 ,判斷pro8的狀態,和步驟7中pro4對於狀態的判斷一致,不經過:將pro8的onfulfilled->value => {console.log('then5:', value) }和pro9的resolve存到pro8的asyncObj中,即此時 pro8.asyncObj={onfulfilled:pro8.onFulfilled,resolve:pro9.resolve},返回pro9,由於pro9沒有then,因此 沒有建立新的pro10 ,promise chain的then或者說同步過程結束,執行promise chain 之後的邏輯,直到參數promise,即pro4的resolve被觸發,才繼續執行promise chain,步驟11結束。
  12. 致此,全部的then都觸發了一遍,執行完成之後總共有 6個顯式then(每一個then都會建立新的promise)+1個初始promise+1個參數promise+1個隱式then(參數promise的then會隱式執行建立一個新的promise)=9個promise,從上面步驟可得 then的執行順序爲:1,2,4,3,6,7,8 異步以前的前兩個promise已經執行完畢, 當前執行的 resolve狀況爲:1,2,3 ,從參數promise即pro4,觸發之後,後面邏輯中的 promise.asyncObj中都存儲了本promise的onfulfilled和該then返回的新promise的resolve。須要特別提醒一下的是 pro3的asyncObj中存儲的是pro的onfulfiled和pro6的resolve,由於pro3.then建立的是pro6 。同時,pro7的asyncObj中存的onfulfilled是undefined。定時器完成,pro4.resolve('then3')進入隊列,等待主棧邏輯執行完畢即觸發,步驟12結束
  13. pro4.resolve('then3')觸發,判斷傳入的值是否爲promise,值'then3'不是promise:pro4.value改變爲pro4.resolve參數'then3',pro4狀態改變,執行setTimeout裏的代碼,由步驟7可知 pro4.asyncObj={onfulfilled:pro3.resolve,resolve:pro5.resolve}先執行 let nnvalue = onfulfilled('then3'),獲得結果之後執行 pro5.resolve(nnvalue),onfulfilled('then3')觸發,onfulfiled爲pro3.resolve,即執行 pro3.resolve('then3'),可是步驟13其實尚未結束,由於pro5.resolve(nnvalue)尚未執行。
  14. pro3.resolve('then3')觸發,判斷傳入的值是否爲promise,值'then3'不是promise:pro3.value改變爲pro3.resolve參數'then3',pro3狀態改變 ,執行setTimeout裏的代碼,由步驟8可知pro3.asyncObj={onfulfilled:pro3.onFulfilled,resolve:pro6.resolve},可是其實這裏還有一步,此時進入setTimeout就是異步執行了,而在步驟13中還有一步沒有執行,哪裏屬於主邏輯,先去執行pro5.resolve(nnvalue),由於let nnvalue = onfulfilled('then3')===pro3.resolve('then3'),pro3.resolve('then3')沒有返回值,因此nnvalue=undefined, pro5.resolve(undefined)執行,判斷參數undefined不是promise,修改pro5.value爲undefined,修改狀態,調出pro5.asyncObj,執行內部函數,由步驟7可知 pro5.then未被調用過,因此其asyncObj爲空,至此步驟13結束,可是步驟14仍在繼續 ,等主流程即步驟13結束之後,步驟14setTimeout內的函數開始執行-> let nnvalue=pro3.onFulfilled('then3')-> let nnValue= (value => {console.log('數據then3', value)return 'then4'})('then3'),獲得nnvalue='then4',而後執行pro3.asyncObj.resolve(nnvalue)pro6.resolve('then4'),其實從13結束之後就開始了遞歸啦,步驟14結束。
  15. pro6.resolve('then4')觸發,判斷參數'then4'是否爲promise,不是:修改 pro4.value='then4',修改狀態,執行定時器,等待主進程結束,執行定時器內部代碼,由步驟9可得 pro6.asyncObj={onfulfilled:pro6.onFulfilled,resolve:pro7.resolve} ,pro6.onfulfilled('then4')->(value=>{console.log('數據then4', value)return 'then5'})('then4'),而後獲得結果nnvalue爲 'then5', 執行 pro6.asyncObj.resolve('then5')pro7.resolve('then5'),步驟15結束
  16. pro7.resolve('then5')觸發,判斷參數'then5'是否是promise,不是:修改pro7.value='then5',修改狀態,執行定時器,等待主進程結束,執行定時器內部代碼,由步驟10可得 pro7.asyncObj={onfulfilled:pro7.onFulfilled,resolve:pro8.resolve} ,由於pro7.onFulfilled=undefined,執行pro7.asyncObj.resolve('then5')pro8.resolve('then5'),步驟16結束
  17. pro8.resolve('then5')觸發,判斷參數'then5'是否是promise,不是:修改pro8.value='then5',修改狀態,執行定時器,等待主進程結束,執行定時器內部代碼,由步驟11可得 pro8.asyncObj={onfulfilled:pro8.onFulfilled,resolve:pro9.resolve} ,執行pro8的onfulfilled->(value => {console.log('then5:', value) })('then5'),獲得 nnvalue=undefined 執行pro8.asyncObj.resolve(undefined)pro9.resolve(undefined),步驟17結束
  18. pro9.resolve(undefined)觸發,判斷參數undefined是否爲promise,不是:修改pro9.value=undefined,修改狀態,執行定時器,由於pro9.then沒有執行過,因此pro9.asyncObj沒有值,因此結束。整個promise鏈也結束。

上面問題的答案

  1. 共有幾個promise,9個
  2. 共有幾個then,執行順序:7個,1,2,4,3,6,7,8
  3. 共有幾個resolve,執行順序:10個,前3個:1,2,3, 後7個:4,3,5,6,7,8,9
相關文章
相關標籤/搜索