---
# System prepended metadata

title: Javascript Promise & async await

---

# 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
}();
```