這一篇我想來學習一下Promise。
為什麼說是學習呢?因為我發現我無法跟人解釋這個東西,因此才決定重新學習Promise
以及他後續延伸出來的async/await
非同步處理的演進
但在講之前我們必須先知道,他為什麼被創造出來,他是為了改變什麼事情。
讓我們先看一下演進圖:
由這邊我們可以得知,在Promise
還沒出來前我們都是使用Callback
來處理非同步的事情,但相信大家一定都聽過恐怖的Callback Hell
吧。
由於使用Callback很容易會產生 callback hell的狀況跟難以維護的code。
因此Promise
就誕生了,他的出現讓我們有:
1.更清晰的code結構:通過Promise,讓非同步操作的部分分離出來,降低了code的複雜性。
2.處理錯誤更容易:主要是因為 Promise提供了.catch()
来捕獲和處理非同步操作中的錯誤。
3.避免再出現Callback Hell
:透過.then()將非同步操作連接起來,讓code更加扁平跟可讀。
4.更好的流程控制:我們可以根據Promise的狀態,來決定下一步的操作。
Promis
什麼是Promis
Promise,如字面的意思就代表 承諾。
網路上有大大幫我們整理好了Promis的流程圖:
圖片來源:https://hackmd.io/@wheat0120/javascript-promise
當我們今天拿到一個Promise 的時候,代表這個 Promise 在之後可能會有幾種狀況發生:
成功 (fulfilled)
用
resolve()
來兌現失敗 (rejected)
用
reject()
來表示失敗還在執行中 (pending)
一直沒有回傳
上述3個承諾我們統稱為:**state
**。
Promise除了有 state
這個屬性外,還有一個屬性我們需要知道,那就是**result
** 。
result
:執行完 Promise 後的結果值。
當決定好了狀態(state)後,就會依照我們所設定好的程式開始執行。
創建Promise
Promise 是一個物件建構子 (constructor),使用時需要先從 Promise 物件產生物件實例 (instance),再使用繼承特性的 instance 去包裝程式碼的 callback 流程。
基本的 Primise 宣告方式如下:
1 | // 建立 instance |
我們可以看到在 Callback 函式內有兩個引數,分別是 resolve 跟 reject 。
這是由 JS 提供、用來決定 Promise 結果狀態時使用的兩個function :
**resolve
**:
- 在 Promise 成功且结果如预期時調用(invoke)。
- 調用(invoke) 此 function 會將 Promise 的 state 設定為 成功的狀態(fulfilled)。
- 執行結果的值會傳遞給此 function,從而設置前面提到的
result
為給定的值。 - 會觸發後續的
.then()
部分來處理成功狀態的Promise。
**reject
**:
- 在 Promise 遭遇錯誤情況時調用(invoke)。
- 調用(invoke) 此 function 會將 Promise 的 state 設定為 失敗的狀態(rejected)。
- 錯誤訊息會做為參數傳遞給此 function。
- 會觸發後續的
.catch()
部分来處理拒絕狀態的Promise。
使用Promise
1 | // 流程控制 |
Promise 是用於進行流程控制的物件 (容器),它具備了 callback 的優點,但透過 .then()
來標明流程,而 .then()
之間可以互相鏈結 (chaining),把之前「一層包一層的 callback」,轉換成 .then()
的串接。
我們來看一段code ,在上面有備註,這樣會更清楚:
1 | var error = false |
驗收
查找資料時看到的案例:
1 | const apiURL = `https://webdev.alphacamp.io/api/lyrics/Coldplay/Yellow.json` |
Answer
1
2
3
4
5
6
7
8
9// 1
// 2
// blocking start
// blocking end
// 3
// 5
// 7
// 4
// 6, res
你答對了嗎?我承認我答錯了XD
我們一步一步來看。
我們執行了promise這個function ,因此console印出了1。
之後我們新建立了一個Promise,在等待確認state的時候遇到第二個console,因此會印出2。
之後執行了 blockingTest()
這個function,他模擬一個長時間運行的同步操作,阻塞了程式的執行,並且遇到了2個console因此印出 “blocking start” 和 “blocking end”。
接下來遇到了fetch
,會發出一個非同步的請求,因此這段會先丟到event queue 等待,程式會繼續往下執行到console.log(5)
並且印出5。
.then()
由於還不確認state的結果會是fulfilled
還是rejected
,因此不執行,程式會繼續往下執行到console.log(7)
並且印出7。
此時event loop監測到stack已經空了,這時候就會把event queue裡正在等待fetch
拉到stack並執行,因為可以確實連接上api,因此.then()
就會被執行,到第二個.then()
就會確認Promise 的 state 是fulfilled
,並且印出4。
因為Promise 的 state 是fulfilled
,因此就會執行
1 | .then(res => { |
最後印出6跟res裡的資料。
終於把失去的記憶給找了回來,接下來還有async/await
要找回關於他的記憶。
參考資料: