---
###### tags: `Game`
---
# 11/30 - Promise、async/await
## Callback function 回調函式
### 什麼是 callback?
- callback 就是將 function 當作參數傳入另外一個函式中
### JavaScript 的同步與非同步
- 同步:就像==接力賽跑==,當棒子沒有交給我,我就得等你,不能跑
- 非同步:就像一般我們在點餐時,服務生會給號碼牌,我就==可以離開櫃台先去逛街==,櫃台也可以繼續為下一個客人點餐,接著只要等號碼牌響起,我再去取餐即可。
```typescript=
// 定義 callback 是一個 function(string):void
function main(callback: (data: string) => void) {
const data: string = "Hello";
callback(data);
}
// 參數 a,b 都是從 main 裡面丟進去的
main(function (a: string) {
console.log(a); // Hello
});
main((b: string) => {
const test = b + "測試";
console.log(b); // Hello
console.log(test); // Hello測試
});
```
## Promise
### 概述
:::info
- 你可以想像你去買橘子,結果店裡沒有進貨,店員對你==Promise==,只要他店裡到貨(fulfilled)或者不再進貨(rejected),==then==他就會通知你
:::
- Promise 是一個構造函數,new Promise 返回一個 promise 物件,接收一個帶有 resolve 和 reject 兩個參數的函數,這個函數在 Promise 構造函數返回所創建 promise 實例對象前被調用。
- ==resolve,reject 是一個函數==,處理結束後調用 resolve 或 reject。
- 當調用 resolve,會把當前 promise 對象狀態由 pending 標記成功(fulfilled),當調用 reject,會把狀態由 pending 標記失敗(rejected)。
- - [參考資料](https://segmentfault.com/a/1190000025180588)
```typescript=
// 接收兩個 function 成功-> then ,失敗-> catch
const promise = new Promise((resolve, reject) => {
// 執行後即回傳,不會往下
reject("失敗");
resolve("成功");
});
promise
.then((res) => {
console.log(res); // 成功
})
.catch((err) => {
console.log(err); // 失敗 - 先回傳才讀得到值
});
```
- Promise.all ( ) 並發處理多個異步任務,所有任務都執行完成才執行回調函數
- Promise.race ( ) 只要有一個任務完成就會執行回調得到結果
```typescript=
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = new Promise((resolve, reject) => {
resolve(2);
});
const p3 = new Promise((resolve, reject) => {
resolve(3);
});
Promise.all([p1, p2, p3, promise]).then((res) => {
console.log(res); // [ 1, 2, 3 ]
});
Promise.race([promise, p1, p2, p3]).then((res) => {
console.log("有一個異步率先完成了", res); //有一個異步率先完成了 成功
});
```
### Promise 的存在意義
```typescript=
// Promise() 同步 executor 同步
const p = new Promise((resolve, reject) => {});
// 異步
p.then((res) => {});
```
:::info
問題:為什麼 Promise 執行是同步,p.then 是異步 ?
:::
- AJAX 為例
```typescript=
// 異步程序
let data = $.ajax({
url: "https://hopsell-api.herokuapp.com/product/all",
async: false, // 改成同步
});
// 會等 1~2 秒資料回傳才繼續往下
console.log(getNames(data.responseJSON.data));
console.log("我看見很多商品");
function getNames(data: any[]) {
return data.map((item) => {
return item.productName;
});
}
```
- 雖然成功將 getNames() 從 AJAX 中抽離,但也造成了後續的程式阻塞
- 顯然不符合我們的需求
```typescript=
// Promise 異步問題同步化解決方案 - 最優解
const promise = new Promise((resolve, reject) => {
$.ajax({
url: "https://hopsell-api.herokuapp.com/product/all",
success(data) {
resolve(data);
},
});
});
// 異步
promise.then((res: any) => {
console.log(getNames(res.data));
});
console.log("我看見很多商品");
function getNames(data: any[]) {
return data.map((item) => {
return item.productName;
});
}
```
## Generator
- generator 和函數不同的是,generator 由 function* 定義(注意多出的*號)
- 並且除了 return 語句,還可以用 yield 返回多次。
```typescript=
// 正常情況
function fib(max: number) {
let a = 0,
b = 1,
arr = [0, 1];
while (arr.length < max) {
[a, b] = [b, a + b];
arr.push(b);
}
return arr;
}
console.log(fib(5)); // [ 0, 1, 1, 2, 3 ] - 回傳陣列
// 採用 Generator
function* fib2(max: number) {
let a = 0,
b = 1,
n = 0;
while (n < max) {
yield a;
[a, b] = [b, a + b];
n++;
}
return;
}
let f = fib2(5);
f.next(); // { value: 0, done: false } - next()後執行一次
f.next(); // { value: 1, done: false }
f.next(); // { value: 1, done: false }
f.next(); // { value: 2, done: false }
```
## async / await
#### 訣竅(Tips)
- 在迴圈中使用 async/await
- 若想要在迴圈中使用 async / await 的寫法需要特別留意, 一般來說在 for, while 或 for...of 這種迴圈中都可以正常使用;但若是在帶有 callback 的迴圈中,例如 forEach, map, filter, reduce 等等,就需要特別留意:
- 如果想要在迴圈中使用 await,則==可以使用一般的 for 迴圈、for await...of,或 map 搭配 Promise 的寫法==,==千萬不要使用 forEach==。
- 若有需要使用 filter 或 reduce 等其他處理陣列的方式,==先等陣列的資料完整取得後再來呼叫這些方法。==