owned this note
owned this note
Published
Linked with GitHub
# 回呼函式 Callbacks、Promises 物件、Async/Await 非同步流程控制
###### tags: `js` `Async` `Promises`
在製作scrollBlog的js實作時,使用到Async/Await 非同步流程控制,讓畫面捲動往下時,可以捲動畫面往下,設定呈現的時間。
接下來的筆記內容主要參考->[彭彭直播](https://www.youtube.com/watch?v=NOprCnnjHm0)
## 題目的三個技巧都是在做同樣的事情
* 解決非同步程式方案
## 什麼時候會使用到非同步流程控制
* setTimeout與網路連線,較常需要用到非同步的技巧
* 和資料庫互動的程式(node)
## 同步程式範例:
* 按下test按鈕,執行`test()`
```htmlembedded
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>非同步流程控制 Callback、Promise、Async/Await</title>
<script>
//
function add(n1, n2) {
return n1 + n2;
}
function test() {
let result = add(3, 4);
console.log(result);
}
</script>
</head>
<body>
<h3>非同步流程控制 Callback、Promise、Async/Await</h3>
<button onclick="test();">Test</button>
</body>
</html>
```
## 非同步的問題起源
* 希望能後延遲兩秒,再得到加法的結果,並印出
* 發現return失效(它是return,setTimeout的function,不是result)
* setTimeout:延遲一段時間,在執行裡面的函式
* 延遲一段時間,再做加法的程式
![](https://i.imgur.com/PY5ZMcq.png)
```javascript
<script>
// 問題的起源:非同步的程式
function delayedAdd(n1, n2, delayTime) {
debugger
// 設定排程,延遲一段時間後執行
window.setTimeout(function () {
console.log(2);
return n1 + n2;
}, delayTime);
console.log(1);
}
function test() {
let result = delayedAdd(3, 4, 2000);
console.log(result);
}
</script>
```
## 解決非同步的問題
1.回呼函式
* 增加第四個參數->callback
* 將加法的結果傳到callback函式中`callback(n1 + n2)`
* 原本`let result = delayedAdd(3, 4, 2000);`,`delayedAdd`回傳值抓不到資料的問題,改為`delayedAdd(3, 4, 2000, function (result) { console.log(result);});`
* 將函式傳遞到參數(callback)當中
```javascript
<script>
// Callback 回呼函式
function delayedAdd(n1, n2, delayTime, callback) {
// 設定排程,延遲一段時間後執行
window.setTimeout(function () {
// 延遲一段時間之後,計算加法,呼叫 callback 函式
callback(n1 + n2);
}, delayTime);
}
function test() {
delayedAdd(3, 4, 2000, function (result) {
console.log(result);
});
}
</script>
```
2.Promise 物件
* 建立 Promise 物件:new Promise(執行函式)
* 將setTimeout欲執行的工作,放入promise裡
* 工作完成,呼叫 resolve 函式,並且把結果透過參數傳遞進去
程式運行流程:
* 按下test按鈕,呼叫`test()`->呼叫`delayedAdd()`
* 進入`delayedAdd()`,建立一個新的promise物件(承諾延遲2秒鐘作加法),物件建立完成後,執行的工作會交由另一個執行序,不等待其完成
* 直接將p回傳,進入`promise.then()`,會接收2秒鐘之後,如果工作完成,就呼叫`resolve(n1 + n2)`,`resolve`會接到`function (result)` -> 結果為7
```javascript
<script>
// Promise 物件
function delayedAdd(n1, n2, delayTime) {
// 建立 Promise 物件:new Promise(執行函式)
let p = new Promise(function (resolve, reject) {
window.setTimeout(function () {
resolve(n1 + n2); // 工作完成,呼叫 resolve 函式,並且把結果透過參數傳遞進去
}, delayTime);
});
return p;
}
function test() {
let promise = delayedAdd(3, 4, 2000);
promise.then(function (result) {
console.log(result);
});
}
</script>
```
```javascript
return p = new Promise(function (resolve, reject) {
window.setTimeout(function () {
resolve(n1 + n2); // 工作完成,呼叫 resolve 函式,並且把結果透過參數傳遞進去
}, delayTime);
});
}
```
* #### reject 處理錯誤的情況
```javascript
// Promise 物件
function delayedAdd(n1, n2, delayTime) {
// 建立 Promise 物件:new Promise(執行函式)
return new Promise(function (resolve, reject) {
window.setTimeout(function () {
reject(n1 + n2); // 工作完成,呼叫 resolve 函式,並且把結果透過參數傳遞進去
}, delayTime);
});
}
function test() {
let promise = delayedAdd(3, 4, 2000);
promise.then(function (result) {
console.log(result);
}).catch(function (error) {
console.log("error", error)
})
;
}
```
![](https://i.imgur.com/KBe7dwY.png)
### Async/Await 語法
* 背後的運作邏輯和promise是一樣的
* 前提:一定有一個函式回傳promise物件
![](https://i.imgur.com/we3DJa9.png)
* 增加await後面加上的函式,必須加上promise物件
* resolve會直接進到result1
* 在函式中用到await,此函式就要宣告`async function`
```javascript
<script>
// Async/Await 語法:簡化 Promise 語法
function delayedAdd(n1, n2, delayTime) {
// 建立 Promise 物件:new Promise(執行函式)
return new Promise(function (resolve, reject) {
window.setTimeout(function () {
resolve(n1 + n2); // 工作完成,呼叫 resolve 函式,並且把結果透過參數傳遞進去
}, delayTime);
});
}
async function test() {
// 分別等待多個 Promise 完成後才繼續動作
let result1 = await delayedAdd(3, 4, 2000);
console.log(result1)
// let result2 = await delayedAdd(2, 3, 3000);
// let answer = result1 * result2;
// console.log(answer);
}
</script>
```
## 試做2次的延遲加法
#### 1.使用promise
* `let promise1 = delayedAdd(3, 4, 2000);`延遲2秒, `let promise2 = delayedAdd(2, 3, 3000);`3+4; 延遲3秒,2+3
* 同時等待多個 Promise(大寫P)->`Promise.all([promise1, promise2]).then()`:等到all裡面的promise都執行完,再執行`.then()`
```javascript
function delayedAdd(n1, n2, delayTime) {
// 建立 Promise 物件:new Promise(執行函式)
return new Promise(function (resolve, reject) {
window.setTimeout(function () {
resolve(n1 + n2); // 工作完成,呼叫 resolve 函式,並且把結果透過參數傳遞進去
}, delayTime);
});
}
function test() {
let promise1 = delayedAdd(3, 4, 2000);
let promise2 = delayedAdd(2, 3, 3000);
// 同時等待多個 Promise 都完成之後,才繼續工作
Promise.all([promise1, promise2]).then(function (results) {
let answer = results.reduce(function (total, value) {
return total * value;
});
console.log(answer);
});
}
```
#### 2.使用Async/Await 語法
```javascript
<script>
// Async/Await 語法:簡化 Promise 語法
function delayedAdd(n1, n2, delayTime) {
// 建立 Promise 物件:new Promise(執行函式)
return new Promise(function (resolve, reject) {
window.setTimeout(function () {
resolve(n1 + n2); // 工作完成,呼叫 resolve 函式,並且把結果透過參數傳遞進去
}, delayTime);
});
}
async function test() {
// 分別等待多個 Promise 完成後才繼續動作
let result1 = await delayedAdd(3, 4, 2000);
let result2 = await delayedAdd(2, 3, 3000);
let answer = result1 * result2;
console.log(answer);
}
</script>
```
## 使用 try catch 做錯誤和例外處理
在 async function 中,使用`try catch`來處理
以上個為例:
```javascript
async function test() {
try {
// 分別等待多個 Promise 完成後才繼續動作
let result1 = await delayedAdd(3, 4, 2000);
let result2 = await delayedAdd(2, 3, 3000);
let answer = result1 * result2;
console.log(answer);
} catch (err) {
console.log(err)
} // TypeError: Failed to fetch
}
```
---
參考資料:
[loupe](http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D)
[回呼函式 Callbacks、Promises 物件、Async/Await 非同步流程控制 - 彭彭直播](https://www.youtube.com/watch?v=NOprCnnjHm0)
[從 JavaScript Promise 到 Async Await](https://w3c.hexschool.com/blog/b6d5db8)
----
* promise
* 若需要明確界定做作之間的順序相依性,就要使用promise。若不在乎何時做完,也不在乎某function之前要先完成哪些function,就不需要使用promise,因為就讓functions各自去執行即可。
* 通常使用promise時,會搭配使用箭頭函式,因為promise跟箭頭函式都同樣是ES6的新功能,可以使用promise就代表可以使用箭頭函式了。