Promise
JavaScript 是屬於同步的程式語言,一次僅能做一件事情,但遇到非同步的事件時,就會將非同步的事件移動到事件儲列,等到所有的原始碼運行完以後才會執行非同步的事件。
Promise
是在 ES6
出現的語法,是用來改善 JavaScript
非同步的語法結構,而 ES7
Async
、Await
可以基於 Promise
讓非同步的語法的結構類似於 “同步語言”,更易讀且好管理。
標準的 Promise 共有三種狀態 (MDN):
- pending(等待中):為初始之狀態,即不是 fulfilled 也不是 rejected。
- fulfilled(實現):表示操作完成,又稱 resolved。
- rejected(拒絕):表示操作失敗。
生命週期
Promise
的生命週期:
- 每個
Promise
都會經過pending
狀態 pending
後分別會有成功時的fulfill
,及失敗時的reject
。- 透過
.then()
在成功時接著處理資料,或是以.catch()
做失敗時的應對。 - 也可以再
return
一個新的Promise
延續處理。
new Promise( /* executor */ function(resolve, reject) { ... } ); |
const promise = new Promise((resolve, reject) => { |
- executor
- 為一個依序接收兩個參數的函式:
resolve
及reject
(實現及拒絕回呼函式)。- 為一個依序接收兩個參數的函式:
executor
函式在傳入參數resolve
與reject
後會立刻執行(會在Promise
建構式回傳Promise
物件前被執行)。resolve
與reject
函式,會在被個別呼叫時,個別執行。- 成功完成後執行
resolve
以完成Promise
;或如果有錯誤,執行reject
。- 成功完成後執行
執行順序
非同步(異步)任務可分為 task
和 microtask
兩類。
另外在 ES2015 規範中稱為
microtask
又被稱為Job
。
- (macro)task 主要包含:
script
(整體代碼)、setTimeout
、setInterval
、I/O
、UI交互事件
、postMessage
、MessageChannel
、setImmediate
(Node.js 環境)。 - microtask(Job) 主要包含:
Promise.then
、MutaionObserver
、process.nextTick
(Node.js 環境)。 - microtask(Job) 執行的順位較 task queue 高。
console.log('script start'); |
- 遇到了
console
語句,直接輸出script start
。輸出之後,script 任務繼續往下執行,遇到setTimeout
,setTimeout
為一個 task queue,會先將其任務移往 event queue 中。 - 遇到
Promise
實例。Promise
構造函數中的第一個參數,是在 new 的時候執行,構造函數執行時,裡面的參數進入執行堆疊執行;而後續的.then
則會被分發到 microtask 的 Promise 隊列中去。所以會先輸出promise1
,然後執行resolve
,將then1
分配到對應隊列。
構造函數繼續往下執行,又碰到setTimeout
,然後將對應的任務分配到對應 event queue 中。 - script任務繼續往下執行,最後只有一句輸出了
script end
,至此,全局任務就執行完畢了。 - 每次執行完一個
macrotask
之後,會去檢查是否存在Microtasks
;如果有,則執行Microtasks
直至清空 Microtask Queue。
此時,只有Promise
隊列中的一個任務then1
,因此直接執行,執行結果輸出then1
。當所有的microtast
執行完畢之後,表示第一輪的循環就結束了。 - 開始第二輪的循環。第二輪循環仍然從
macrotask
開始。此時,有兩個macrotask
:timeout1
和timeout2
。 - 取出
timeout1
執行,輸出timeout1
。此時 event queue 中已經沒有可執行的任務了。 - 開始第三輪循環。第三輪循環依舊從
macrotask
開始。此時macrotask
中只有一個timeout2
,取出直接輸出。
return Promise
另一種寫法,return
一個 Promise
值,在這種情況下,放在 return
中的 Promise
會「立即」被解開,由那個 Promise
的 fulfillment
或 rejection
作為解析結果傳遞給下一個 then
。
const promise = new Promise((resolve, reject) => { |
觀念
new Promise(resolve => { |
- 執行
promise
建構函式,會先輸出console.log(3)
, 而console.log(2)
&console.log(t)
會先移至 microtask 等待執行。 - 輸出
console.log(4)
。全局任務執行完畢。 - 執行 microtask 內的任務,先輸出
console.log(2)
在輸出console.log(1)
Promise.resolve
方法允許調用時不帶參數,直接返回一個 resolved 狀態的 Promise 對象。立即 resolved 的 Promise 對象,是在本輪“事件循環”(event loop)的結束時,而不是在下一輪“事件循環”的開始時。- 所以,
console.log(2)
比console.log(1)
會先進入 microtask 的 Promise 隊列。
- 代碼執行完畢。
new Promise(resolve => { |
Promise API
-
Promise.all( [promise1, promise2, …] )
Promise.all
接受一個promise陣列,會在陣列中的promise都傳回fulfillment訊號後,執行下面的then的第一個function (成功),傳遞給then的參數是一個帶有解析值的陣列,如果有其中一個訊號是rejection,就會執行then第二個function (失敗)。 -
Promise.race( [promise1, promise2, …] )
promise.race
接受一個陣列,會在收到第一個訊號(無論是fulfillment或是rejection)後執行下面的then,不管其他之後才收到的訊號。 -
Promise.resolve()
Promise.resolve(value);
Promise.resolve(promise);
Promise.resolve(theanable);
這三種形式都會產生一個新的Promise。其中:
參考
從一道題淺說 JavaScript 的事件循環
自己動手實現 ES6 Promise
promise-介紹與使用