ES6 Promise運行機制解析

內容參考:

阮一峰 - JS運行機制
MDN - Promise構造函數
MDN - 使用Promise
美團Promise解析

偶然回憶起js異步操作的點點滴滴,又想起用了一年的Promise,哎?兄弟,你到底是個什么東西???

先擺個你一定看不懂的圖然后我們慢慢來。(看不懂千萬不要假裝騙自己哦~)


Promise.png

單線程

JavaScript是單線程機制,為什么是單線程因為如果存在兩個線程同時修改dom,dom會不知所措(Android系統也是只允許在主線程中更新UI)
所以既然沒有多線程做并發(fā),只能設計出異步任務。
JavaScript的所有網絡操作,瀏覽器事件,都必須是異步執(zhí)行,那么我們怎么做異步任務呢?

1 ES5回調函數
2 ES6 Promise
3 ES7 async/await

回調函數我們很熟,也知道它的著名缺陷 - 回調煉獄

async1(function(){
    async2(function(){
        async3(function(
            async4(funciton(){
                async5(function(){
                    //(╯°°)╯︵┻━┻
                    //...
                });
            });
        )); 
    });});

Promise 用鏈式調用解決了這一煩惱:

function doingSome(a){
    return new Promise(function(resolve,reject){
            setTimeout(function(){
                resolve(a+1)
            },1500)
            })
}
doingSome(1).then(function(newResult){
    console.log('第一個promise :' + newResult)
    return doingSome(newResult)
}).then(function(result){
    console.log('第二個promise:'+result)
})

But

知道這些可以滿足我們的好奇心么 ???
它的設計模式,代碼結構才是我鐘意的地方。
去MDN翻翻Promise構造方法的參數,方法,執(zhí)行描述,額,疑問一大堆啊,其中有一句:

Promise 對象是一個代理對象(代理一個值),被代理的值在Promise對象創(chuàng)建時可能是未知的。它允許你為異步操作的成功和失敗分別綁定相應的處理方法(handlers)。 這讓異步方法可以像同步方法那樣返回值,但并不是立即返回最終執(zhí)行結果,而是一個能代表未來出現的結果的promise對象

啥,我們創(chuàng)建一個Promise后,它返回一個新的Promise,為什么不返回自己然后重用?

Promise構造函數執(zhí)行時立即調用executor 函數,executor 函數在Promise構造函數返回新建對象前被調用, resolve 和 reject 兩個函數作為參數傳遞給executor。resolve 和 reject 函數被調用時,分別將promise的狀態(tài)改為fulfilled(完成)或rejected(失敗)

所以說executor到底是什么時候被調用的,resolve和reject做了什么騷操作?

then和catch在executor執(zhí)行后被執(zhí)行,那么failureCallback和successCallback在executor執(zhí)行時還沒傳入,那如果我們的executor中直接同步調用resolve或者reject呢?

new Promise(function(resolve, reject){
  resolve()
}).then(function(){
  // here is successCallback
})

試驗結果還是可以調用到successCallback,執(zhí)行executor時還在構造函數方法中,都還沒調用then方法,你怎么得到successCallback的???

先做一些小實驗測試一下
運行結果.png
首先Promise構造函數并沒有返回值

executor調用了resolve后,將-當前-Promise對象狀態(tài)改為了:resolved

接著實驗:


image.png

這里可以確定then方法返回了一個新的Promise
我們去除延遲,再試一遍:


image.png

在then方法內部就把successCallback給執(zhí)行了,最后返回新的Promise對象。
這樣看來resolve感覺像是做了setTimeout 0處理,使它處于任務隊列底部,最后被執(zhí)行。

我們怎么知道它的真正實現呢?源碼找不到,我們看看別人博客吧,我看到很多什么自己實現一個Promise這種文章,但是呢,他們的resolve實現都是用setTimeout 0,我就很疑惑,難道瀏覽器也是這么實現的?

其實不準確,我們看規(guī)范好吧,Promise A+規(guī)范
這里有一段話很重要(google翻譯后的)

image.png

瀏覽器就是用setTimeout,Nodejs就是process.nextTick(所提及的任務機制暫時不懂,不過可以參考:JS并發(fā)模型

那resolve的實現過程就應該是這樣的:

function(val) {
  var _this= this;
  if (this.status == 'pending') {
    this.status = 'resolved';
    this.value=val;
    setTimeout(function() {
      _this.resolveFunc(_this.value);
    }, 0);
  }
}

MDN有一段話也可以幫助我們理解:


image.png
為什么then要返回一個新的Promise?

MDN 對于then方法的解釋:


image.png

如果successCallback或者failureCallback返回的是一個Promise呢 ?
還有一段解釋:


image.png

我想說如果你在回調函數中還返回了一個Promise,就像我們上面用的鏈式調用的例子:
doingSome(1).then(function(newResult){
    console.log('第一個promise :' + newResult)
    return doingSome(newResult)
}).then(function(result){
    console.log('第二個promise:'+result)
})

那么恭喜你,我們這里出現了三個Promise,我們要分析三個Promise在一次鏈式調用中的作用。。。
我們解釋下:“任何被添加給 promise2 的回調函數都會被排在 successCallback 或 failureCallback 返回的 Promise 后面”

上面第二個then添加了一個successCallback,它說會被我們第一個then的successCallback返回的doingSome中的Promise調用,非常亂,不錯有人已經幫我理清楚了,沒錯,我們借鑒一下:

徹底理解Promise對象——用es5語法實現一個自己的Promise(上篇)

我們看下他的then方法怎么寫的:

MyPromise.prototype.then = function(resolveFunc, rejectFunc) {
  var self = this;
  return new MyPromise(function(resolve_next, reject_next) {
    function resolveFuncWrap() {
      var result = resolveFunc(self.value);
      if (result && typeof result.then === 'function') {
        //如果result是MyPromise對象,則通過then將resolve_next和reject_next傳給它
        result.then(resolve_next, reject_next);
      } else {
        //如果result是其他對象,則作為參數傳給resolve_next
        resolve_next(result);
      }
    }
    function rejectFuncWrap() {
      var result = rejectFunc(self.value);
      if (result && typeof result.then === 'function') {
        //如果result是MyPromise對象,則通過then將resolve_next和reject_next傳給它
        result.then(resolve_next, reject_next);
      } else {
        //如果result是其他對象,則作為參數傳給resolve_next
        resolve_next(result);
      }
    }
    self.resolveFunc = resolveFuncWrap;
    self.rejectFunc = rejectFuncWrap;
  })
}

最開始調用的Promise我們這里稱為Promise_1,then中直接return的Promise我們稱為 Promise_2,回調函數中返回的Promise我們稱為Promise_3

Promise_2的目的就是鏈式then的書寫,因為then返回的是一個Promise自然可以執(zhí)行then方法。
內部的邏輯就是封裝callback,讓callback執(zhí)行的時候可以執(zhí)行Promise_2的then的successCallback

這個“讓callback執(zhí)行的時候可以執(zhí)行Promise_2的then的successCallback”就對應了兩種情況:

  1. Promise_1的callback返回了Promise
    我們關鍵代碼:
result.then(resolve_next, reject_next);

將Promise_2和Promise_3神奇的串在了一起,
如果Promise_3調用了resolve,那么就執(zhí)行Promise_2的resolve,Promise_2的resolve不就接著執(zhí)行Promise_2的successCallback么,最后等同于:Promise_2 的successCallback變成了Promise_3的


  1. 返回了非Promise
    直接執(zhí)行Promise_2的successCallback或者failureCallback

錯誤傳播的特性

基本上,一個 Promise 鏈式遇到異常就會停止,查看鏈式的底端,尋找catch處理程序來代替當前執(zhí)行。在同步的代碼執(zhí)行之后,這是非常模型化的。
這化作了兩個特性:

  1. 遇到異?;蛘哒{用reject會去順著鏈式向下找一個catch執(zhí)行
  2. 如果遇到異?;蛘哒{用reject,會直接執(zhí)行catch然后繼續(xù)catch下面的代碼,catch之前都不會執(zhí)行
    舉例:
function doingSome(a){
    return new Promise(function(resolve,reject){
            setTimeout(function(){
                reject(a+1)
            },1500)
            })
}
doingSome(1).then(function(result){
    console.log(result)
    return result
}).then(function(result){
    console.log(result)
    return result
}).then(function(result1){
    console.log(result)
    return result
}).catch(function(result){
    console.log('failed: ' + result)
    return result
}).then(function(result){
    console.log(result)
    return result
})

輸出:
failed: 2
2


(別的實現細節(jié)暫且不提,錯誤請指正?。?/p>

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

相關閱讀更多精彩內容

  • 本文適用的讀者 本文寫給有一定Promise使用經驗的人,如果你還沒有使用過Promise,這篇文章可能不適合你,...
    HZ充電大喵閱讀 7,455評論 6 19
  • title: promise總結 總結在前 前言 下文類似 Promise#then、Promise#resolv...
    JyLie閱讀 12,418評論 1 21
  • Promise 對象 Promise 的含義 Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函...
    neromous閱讀 8,834評論 1 56
  • Promise含義 Promise是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更強大。所謂Pr...
    oWSQo閱讀 1,137評論 0 4
  • 2018.4.18 #每日一問# 除了看紙質書,你是否還有可以推薦的其他閱讀平臺和聽書平臺?(請結合自己的經驗來說...
    訥言敏行2020閱讀 153評論 0 1

友情鏈接更多精彩內容