# Promise, Async / Await
## 前言: 程式碼到底是怎麼跑的?

#### 【1】Execution Context 執行環境
> 所有程式碼都必須在環境(Execution Context)中執行,而執行環境又分兩種:
- Global Execution Context 執行全域環境(一開始)
- Function Execution Context 執行函式環境(當函式被呼叫)
> 每個環境的執行都會經歷兩個階段:
> - Creation Phase 創造階段
> 
> - Execution Phase 執行階段
> 一行一行讀取程式碼,如果在這個環境下找不到需要的參數,就會繼續往外層、往全域一層一層的去找,如果正常的執行了,那麼這個執行環境就會跳出 (pop off) 執行堆疊 (stack) 中。

:::spoiler 詳細創造環境流程

:::
#### 【2】Execution stack 執行堆疊
>- JavaScript 是單線程(single threaded runtime)的程式語言,所有的程式碼片段都會在堆疊(stack)中被執行,而且一次只會執行一個程式碼片段(one thing at a time)。
>- 他會優先處理最上層的環境,而正在執行中的環境又被稱為 現行環境 (Active context)。
>- 執行到每一個函式中的 return 或結尾時,這個函式便會跳離(pop off)堆疊。

#### 【!】Blocking 阻塞
> - 當執行程式碼片段需要等待很長一段時間,或好像「卡住」的一種現象,以網站為例,當堆疊被阻塞(stack blocked),會使瀏覽器無法繼續渲染頁面,導致頁面「停滯」。
> - 為了要解決堆疊被阻塞的問題,我們會使用非同步的方法。
:::info
**Asynchronous 非同步**
雖然 JavaScript的執行環境(Runtime)一次只能做一件事,但瀏覽器提供了更多不同的 API 讓我們使用,進而讓我們可以透過 event loop 搭配非同步的方式同時處理多個事項。
:::
#### 【!】Event Queue 事件佇列(瀏覽器提供)
> 專門用來存放這些非同步的函式,等到整個主執行環境運行結束以後,才會開始依序執行事件儲列裡面的函式。(Event Queue 與 Stack 執行順序相反,先進來的會先被執行)
#### 【!】Event Loop 事件循環(瀏覽器提供)
> 事件循環(event loop)的作用很簡單,如果堆疊(stack)是空的,它便把佇列(queue)中的第一個項目放到堆疊當中;堆疊(stack)便會去執行這個項目。
:::warning
困擾的開始:非同步的流程控制
:::
```javascript=
function delayAdd(n1,n2,delayTime){
window.setTimeout(function(){
return n1+n2
},delayTime)
}
function test(){
let result=delayAdd(3,4,2000)
console.log(result)
}
test() //undefinded🥺
```
## Asynchronous Callbacks 非同步回呼
> 回呼函式常常被使用在非同步的流程完成後需執行的動作,該情境被稱為「 asynchronous callbacks 」。
#### 補充: Callback 回呼函式
>函式中傳入的參數為函式的形式時,該函式參數即為「 回呼函式 」。
>A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.
### 常見非同步回呼
- **計時器**
```javascript=
// setTimeout: 1秒後執行callback函式
window.setTimeout(callback,100)
// setInterval: 每1秒執行一次callback函式
window.setInterval(callback,100)
```
- **事件處理器**
```javascript=
button.addEventListener("click",callback)
```
- **網路請求**
```javascript=
// XMLHttpRequest
function getCurrentVersionNumber(versionCallback){
// 發送網路請求
let request = new XMLHttpRequest();
request.open("GET","http://www.example.com");
request.send();
// 請求成功
request.omload=function(){
if (request.readyState === 4){
if(request.status===200){
let currentVersion = parseFloat(request.responseText)
versionCallback(null,currentVersion)
}
else{
versionCallback(response.statusText,null)
}
}
}
// 請求失敗
request.onerror = request.ontimeout=function(e){
versionCallback(e.type,null)
}
}
```
**`request.readyState`: 處理階段**
>0:尚未讀取
>1:讀取中
>2:已下載完畢
>3:資訊交換中
>4:處理完畢
**`request.status`: HTTP協定的狀態碼**
> 200:連線成功
> 404:連線錯誤
### 😈 Callback Hell 回呼地獄
- 巢狀結構的 callback function 語法。(一個callback裡面還有其他callbacks)
- 為了強制流程的順序,需大量使用判斷式。
- 傳入過多的 CallBack function。


## ES6 Promise 物件
> 表示「 單一 」非同步運算的最終完成或失敗的物件。
> (因此不會在將Promise用於按鈕事件等等,需重複觸發的情境)
- 語法結構較為線性(Promise Chain),容易閱讀。
- 標準化錯誤的處理方式。
- 串接Promise的技巧:Promise.all, Promise.race。
```javascript=
// callback寫法
function delayAdd(n1,n2,delayTime,callback){
window.setTimeout(function(){
callback(n1+n2)
},delayTime)
}
function test(){
delayAdd(3,4,2000,function(result){
console.log(result)
})
}
// Promise物件
function delayAdd(n1,n2,delayTime){
return new Promise(function(resolve,reject){
window.setTimeout(function(){
resolve(n1+n2)
},delayTime)
}
function test(){
let promise=delayTime(3,4,2000)
promise.then(function(result){
console.log(result)
})
}
```
## ES7 async / await
>- async / await 是一個簡化Promise的語法糖。
>- async 用來宣告一個非同步函式,讓這個函式本體是屬於非同步,但以看似同步的方式運行程式碼。
>- await 則可以暫停非同步函式的運行(中止 Promise 的運行),直到非同步流程結束。
- async(function),await(運算子) 會一起被使用。
- 使用 async 宣告的函式必須為Promise物件
- 由於宣告為 async 的函式會被視為一個同步函式,所以當發生錯誤時,程式會全部停止運作!因此通常會搭配 try...catch 使用。
```javascript=
function delayAdd(n1,n2,delayTime){
return new Promise(function(resolve,reject){
window.setTimeout(function(){
resolve(n1+n2)
},delayTime)
}
async function test(){
try{
let result = await delayTime(3,4,2000) //7
console.log(result)
}
catch(error){
console.log("error",error)
}
})
}
```
### 資料參考
[回呼函式 Callbacks、Promises 物件、Async/Await 非同步流程控制 - 彭彭直播](https://www.youtube.com/watch?v=NOprCnnjHm0&ab_channel=%E5%BD%AD%E5%BD%AD%E7%9A%84%E8%AA%B2%E7%A8%8B)
[MDN Promise](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Using_promises)
[秒懂!JavaSript 執行環境與堆疊](https://medium.com/%E9%AD%94%E9%AC%BC%E8%97%8F%E5%9C%A8%E7%A8%8B%E5%BC%8F%E7%B4%B0%E7%AF%80%E8%A3%A1/%E6%B7%BA%E8%AB%87-javascript-%E5%9F%B7%E8%A1%8C%E7%92%B0%E5%A2%83-2976b3eaf248
)
https://hsiangfeng.github.io/javascript/20190530/3821927811/
https://itbilu.com/javascript/js/41KMSZ9a.html
https://cythilya.github.io/2018/10/31/promise/
https://pjchender.blogspot.com/2017/08/javascript-learn-event-loop-stack-queue.html