# Javascript Promise & async await ### 開始之前幾件事要先知道 * Javascript 是單線程(single thread) > 所有的程式碼片段都會在堆疊中(stack)被執行,而且一次只會執行一個程式碼片段 * JavaScript 是一個「非同步」的語言 > 同步任務:在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務。 > 非同步任務:不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務可以執行了,該任務才會進入主線程執行。 > 事件迴圈(Event Loop):只有執行棧中的所有同步任務都執行完畢,系統才會讀取任務佇列,看看裡面的非同步任務哪些可以執行,然後那些對應的非同步任務,結束等待狀態,進入執行棧,開始執行。 > ![](https://i.imgur.com/XDOckKG.png) --- > **(1)所有同步任務都在主線程上執行,形成一個執行棧(execution context stack)。 > (2)主線程之外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。 > (3)一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務隊列",看看裡面有哪些事件。那些對應的異步任務,於是結束等待狀態,進入執行棧,開始執行。 > (4)主線程不斷重複上面的第三步** --- > JS的單執行緒是指一個瀏覽器程序中只有一個JS的執行執行緒,同一時刻內只會有一段程式碼在執行(你可以使用IE的標籤式瀏覽試試看效果,這時開啟的多個頁面使用的都是同一個JS執行執行緒,如果其中一個頁面在執行一個運算量較大的function時,其他視窗的JS就會停止工作)。 > 而非同步機制是瀏覽器的兩個或以上常駐執行緒共同完成的,例如非同步請求是由兩個常駐執行緒:JS執行執行緒 和 事件觸發執行緒 共同完成的,JS的 執行執行緒 發起非同步請求(這時瀏覽器會開一條新的 HTTP請求執行緒 來執行請求,這時JS的任務已完成,繼續執行執行緒佇列中剩下的其他任務),然後在未來的某一時刻 事件觸發執行緒 監視到之前的發起的HTTP請求已完成,它就會把完成事件插入到JS執行佇列的尾部等待JS處理。又例如定時觸發(settimeout和setinterval)是由瀏覽器的 定時器執行緒 執行的定時計數,然後在定時時間把定時處理函式的執行請求插入到JS執行佇列的尾端(所以用這兩個函式的時候,實際的執行時間是大於或等於指定時間的,不保證能準確定時的)。所以,所謂的JS的單執行緒和非同步更多的應該是屬於瀏覽器的行為 > ![](https://res.cloudinary.com/practicaldev/image/fetch/s--0rAYbE6N--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://res.cloudinary.com/practicaldev/image/fetch/s--0TQg9sD0--/c_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000/https://thepracticaldev.s3.amazonaws.com/i/ek7ji4zrimozpp2yzk0a.png) > event loop reference : https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif ### What is Promise > Promise 物件代表一個即將完成、或失敗的 **非同步操作**,以及它所產生的值。 ```javascript= const promise1 = new Promise(function(resolve, reject) { if (true) { setTimeout(function(){ // 3 秒時間後,透過 resolve 來表示完成 resolve('3 秒時間(fulfilled)'); }, 3000); } else { // 回傳失敗 reject('失敗(rejected)') } }); console.log(promise1); // [object Promise] promise1.then(function(value) { // 成功訊息 (需要 3 秒) console.log(value); }).catch((err)=> { // 失敗訊息 (立即) console.log(err) }); ``` > 以上就是一個基本的 Promise 範例,執行 Promise 建構式時還會再帶入 resolve >與 reject 的 callback function。 > * resolve: 完成的 callback > * reject: 失敗的 callback > #### Promise.prototype.then(onFulfilled, onRejected) > 繫結實現或拒絕回呼函式到 promise,回傳一個以 handler 之回傳值作解析的新 promise,或者當 promise 未處理(not handled)時以原值作解析。(i.e. 比如相關聯的 onFulfilled 或 onRejected 不是函式。) > #### Promise.prototype.catch(onRejected) > 繫結一個拒絕回呼函式(rejection handler callback)到 promise,當它被呼叫時回傳一個以回傳值作解析的新 promise,或者當 promise 被實現時以原值作解析。 ### Promise 生命週期 >一個 Promise 物件處於以下幾種狀態: > * 擱置(pending):初始狀態,不是 fulfilled 與 rejected。 > * 實現(fulfilled):表示操作成功地完成。 > * 拒絕(rejected):表示操作失敗了。 > 若一個 promise 已被實現或拒絕,繫結(attached)於它的處理函式(then or catch)將立即被呼叫 > > #### Promise.prototype.then(onFulfilled, onRejected) > then() 方法回傳一個 Promise 物件。它接收兩個引數: Promise 在成功及失敗情況時的回呼函式。 > #### Promise.prototype.catch(onRejected) ```javascript= const delay = (s) => { return new Promise(resolve => { setTimeout(resolve,s); }); }; delay().then(() => { console.log(1); // 顯示 1 return delay(1000); // 延遲ㄧ秒 }, (errorMessage) => { console.log(errorMessage); }) .then(() => { console.log(2); // 顯示 2 return delay(2000); // 延遲二秒 }) .then(() => { console.log(3); // 顯示 3 }) .catch(err => { console.log(err); }); ``` > #### Promise.all(iterable) > 回傳一個 promise,當在引數 iterable 中所有 promises 都被實現時被實現,或在引數 iterable 中有一個 promise 被拒絕時立刻被拒絕。若回傳的 promise 被實現,它將以一個實現值的陣列被實現,其順序與 iterable 中的 promises 相同。若回傳的 promise 被拒絕,它將以失敗訊息被拒絕,此訊息來自第一個在 iterable 中被拒絕的 promise。這個方法在聚集許多 promises 的結果時很有效。 > #### Promise.race(iterable) > 回傳一個被實現或拒絕的 promise,當 iterable 中有一個 promise 被實現或拒絕時。 > 注意: await如果遇到錯誤就會造成停止讓後方程式碼不執行 ```javascript= ~async function{ // ~ 開頭表示直接執行這個 function,結尾有 () const delay = (s) => { return new Promise(function(resolve){ // 回傳一個 promise setTimeout(resolve,s); // 等待多少秒之後 resolve() }); }; console.log(1); // 顯示 1 await delay(1000); // 延遲ㄧ秒 console.log(2); // 顯示 2 await delay(2000); // 延遲二秒 console.log(3); // 顯示 3 }(); ```