# Javascript Promise & async await
### 開始之前幾件事要先知道
* Javascript 是單線程(single thread)
> 所有的程式碼片段都會在堆疊中(stack)被執行,而且一次只會執行一個程式碼片段
* JavaScript 是一個「非同步」的語言
> 同步任務:在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務。
> 非同步任務:不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務可以執行了,該任務才會進入主線程執行。
> 事件迴圈(Event Loop):只有執行棧中的所有同步任務都執行完畢,系統才會讀取任務佇列,看看裡面的非同步任務哪些可以執行,然後那些對應的非同步任務,結束等待狀態,進入執行棧,開始執行。
>

---
> **(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的單執行緒和非同步更多的應該是屬於瀏覽器的行為
> 
> 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
}();
```