深入學習ES6 Promise

Promise是es6中異步操作對象,在學習Promise之前我們首先要了解Javascript的一些有關異步操作、JS事件機制等方面的知識,這樣才能更好的吸收今天所講的內容。所以先從瀏覽器的進程講起!

瀏覽器進程

瀏覽器是以多進程運行的,而我們的JS引擎是瀏覽器渲染進程中的一個線程(單線程),所謂的單線程就是一次只能執行一個任務,如果有多個任務需要處理的話,那麼就需要排隊,等待上一個任務執行完畢纔會繼續往下執行。這樣就會有個弊端,就是如果當前執行的任務很耗時,那麼等待的任務就會長時間的等待被執行,會出現一種假死(卡死)的狀態,用戶體驗很不好。下面來看看瀏覽器都有哪些進程:

 

根據上面的圖就很清楚的知道,我們平時寫的JS代碼是由JS引擎線程去處理的,而JS引擎線程是一個單線程,所以一次只能執行一個任務,爲了改變這種不友好的用戶體驗,這時就會用到異步這個操作。所謂的JS異步並不是交由JS引擎去完成的,而是交給瀏覽器的其他線程去完成。JS異步操作還會涉及到JS事件循環機制。

JS事件循環機制 

下圖就是JS事件循環機制的一個執行流程:

所謂的JS事件循環機制其實可以這麼理解,當JS引擎去執行JS代碼的時候會從上至下按順序執行,當遇到異步任務的,就會交由瀏覽器的其他線程去執行,如果是setTimeout/setInterval定時異步任務,瀏覽器的渲染進程就會開一個定時器觸發線程去執行,當定時時間一到,就會通知事件觸發線程將定時器的回調方法推送至事件任務隊列的一個宏任務隊列的列尾,等待JS引擎執行完同步任務後,再從事件任務隊列中從頭取出要執行的回調方法。其他異步任務也是這麼一個流程。這就是所謂的JS事件循環。這其中還涉及到了宏任務和微任務的一個概念,那什麼是宏任務?什麼是微任務呢?

宏任務與微任務 

下面的圖會很清楚的告訴大傢什麼是宏任務及微任務:

我們發現今天要講的Promise其實就是一個異步的微任務。宏任務與微任務的一個執行過程是這樣的:

當JS引擎從任務隊列中取出一個宏任務來執行,如果執行過程中有遇到微任務,那麼執行完該宏任務就會去執行宏任務內的所有微任務。然後更新UI。後面就是再從任務隊列中取出下一個宏任務來繼續執行,以此類推。這是ES6的常考的一個知識點,希望大家一舉拿下。

 

到這裏或許大家會有些迷,不過不要緊,多看幾次,或者去查些資料,爭取拿下它。前面講了那麼多主要是將下面要講的Promise貫穿起來,形成一個知識體系。而不是片面的瞭解它,或者只瞭解一部分,搞不清楚它是怎麼串起來的。這也是我在百度上發現的一個痛點,很多知識點你搜的時候大部分文章講的只有一部分,而且不同的文章得出的結論還不一樣,所以我經過查閱大量的資料才總結出上面的這個知識點,目的只有一個,就是要把要學的東西弄清楚,知根知底那纔是真的學到。

 

上面所講的知識點可以總結爲:瀏覽器的運行是靠幾個進程相互配合工作的,JS引擎是瀏覽器渲染進程中的一個線程,它屬於單線程,一次只能執行一個任務,如果有多任務則需要排隊等待被執行,如需要執行異步操作就需要藉助瀏覽器的其他線程完成,異步執行完成後將回調函數推送至任務隊列中,待同步任務執行完畢後,再從任務隊列中取出異步回調方法執行。任務隊列中還可分爲宏任務和微任務,如果宏任務中有微任務,那麼當執行完宏任務,就立即執行微任務,然後纔是更新UI。

 Promise

 Promise 是異步編程的一種解決方案,比傳統的解決方案 (回調函數和事件)更合理和更強大。我們可以簡單的把它理解爲一個容器,它裏面裝的是一個異步操作(某個未來纔會結束的事件)的結果。Promise操作後返回的對象還是一個新的Promise對象,所以支持鏈式調用,它可以把異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數,更便於理解與閱讀。

Promise主要有以下特點:

1.Promise對象狀態不受外界影響,它有三種狀態:

pending:進行中

fulfilled:已成功

rejected:已失敗

只有異步操作的結果才能確定當前處於哪種狀態,任何其他操作都不能改變這個狀態。這也是Promise(承諾)的由來。

 

2.Promise狀態一旦改變就不會再變,任何時候都可以得到這個結果。它的狀態改變只有兩種結果:

1、從pending狀態變爲fulfilled狀態

2、從pending狀態變爲rejected狀態

只要有其中一種情況發生,狀態就凝固了,不會再變,會一直得到這個結果,後續再添加Promise的回調函數也只能拿到前面狀態凝固的結果

 

Promise缺點:

 

1.無法取消Promise,一旦新建它就會立即執行,無法中途取消

2.如果不設置回調函數(沒有捕獲錯誤),Promise內部拋出的錯誤,不會反應到外.

3.當處於pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)

 

講解Promise還會貫穿異步、同步、串行、並行等這幾個慨念:

異步:多個任務可以同時執行,各不影響。

同步:多個任務必須排隊,等待一個一個的執行。

串行:任務按順序執行,前一個任務的結果可以是下個任務的參數,所以必須等待前一個任務執行完畢才能往下繼續。

並行:多個任務可以同步執行,各不相關,實際就是一個異步操作的過程。

 

Promise API

 

 由上圖知,Promise既是一個對象也是一個構造函數,下面就具體分析它的每個api:

Promise.prototype.constructor()

 

它的基本用法如下:

 

Promise接收一個函數作爲參數,函數裏有resolve和reject兩個參數,這兩個參數其實是Promise內置的兩個方法,會在異步操作執行結束後調用,可以將異步操作的結果回傳至回調函數,以確定Promise最終的一個狀態(是fulfilled還是rejected)。

resolve方法的作用是將Promise的pending狀態變爲fulfilled,在異步操作成功之後調用,可以將異步返回的結果作爲參數傳遞出去。

reject方法的作用是將Promise的pending狀態變爲rejected,在異步操作失敗之後調用,可以將異步返回的結果作爲參數傳遞出去。

他們之間只能有一個被執行,不會同時被執行,因爲Promise只能保持一種狀態。

Promise.prototype.then()

Promise實例確定後,可以用then方法分別指定fulfilled狀態和rejected狀態的回調函數。它的基本用法如下:

then(onfulfilled,onrejected)方法中有兩個參數,兩個參數都是函數,第一個參數執行的是resolve()方法(即異步成功後的回調方法),第二參數執行的是reject()方法(即異步失敗後的回調方法)(第二個參數可選)。它返回的是一個新的Promise對象。

 

一般推薦使用第二種寫法,第二種寫法能達到第一種寫法的效果,但是二種寫法還可以捕獲onfulfilled執行過程中拋出的錯,而第一種寫法的onrejected方法無法捕獲onfulfilled中拋出的錯,具體代碼如下:

如果Promise內部拋出的錯不被捕獲是不會反應到外部的:

then還可以很好的解決異步串行的操作,避免了傳統異步串行操作層層嵌套的問題:

異步串行操作,主要思想是使各異步操作按一定順序來執行,可以使用前一個異步操作的結果作爲後一個異步的參數,嚴格確保前一個異步執行完畢後,再執行下一個異步

Promise.prototype.catch()

catch方法是.then(null,onrejected)的別名,用於指定發生錯誤時的回調函數。作用和then中的onrejected一樣,不過它還可以捕獲onfulfilled拋出的錯,這是onrejected所無法做到的:

它的多種寫法如下:

Promise錯誤具有"冒泡"的性質,如果不被捕獲會一直往外拋,直到被捕獲爲止:

他們都只能捕獲前面Promise拋出的錯,而無法捕獲在他們後面的Promise拋出的錯:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章