JavaScript筆記 目錄
非同步是 JavaScript 最重要的觀念之一。
什麼是回呼函式
回呼函式(callback function)是指能藉由參數(argument)通往另一個函式的函式。
簡單來說,就是這個函式做為另一個函式的引數傳入,就稱為「回呼函式」,例如常見的陣列處理方法都有回呼函式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function foo(name) {
console.log(name);
}
function boo(name, callback) {
callback(name);
}
boo('Peter', foo); // "Peter"
// 陣列批次處理方法
const arr = [1, 2, 3];
const newArr = arr.map((item) => item * 2);
console.log(newArr); // [2, 4, 6]
|
這些屬於同步回呼,因為它是立即執行的。
非同步
而回呼常用來延續非同步行動完成後的程式執行,例如計時器等等。
1
2
3
4
|
// 計時器
setTimeout(() => {
console.log('1');
}, 3000);
|
1. 回呼中回呼
舉例來說,如果要設定計時器,在它執行完後,在一次呼叫一個計時器。
錯誤寫法:
1
2
3
4
5
6
7
8
9
10
11
|
setTimeout(() => {
console.log('1');
}, 2000);
setTimeout(() => {
console.log('2');
}, 2000);
// 兩秒後 同時印出
// 1
// 2
|
必須將第二個計時器寫在第一個回呼函式中:
1
2
3
4
5
6
|
setTimeout(() => {
console.log('1');
setTimeout(() => {
console.log('2');
}, 2000);
}, 2000);
|
2. 回呼地域
非同步程式的回呼對於一個或兩個的簡單巢狀,這樣的呼叫看起來還好。
但對於一個接一個非同步動作來說,程式碼會變得非常難以維護。舉例來說,這是一個延遲呼叫的函式:
1
2
3
4
5
6
7
8
9
|
function delay(n, callback) {
setTimeout(() => {
callback(n);
}, 2000);
}
delay(1, (n) => {
console.log(n);
});
|
上面有說過,如果要重複呼叫,就必須要等到上次的呼叫成功時,才能進行下次呼叫:
1
2
3
4
5
6
|
delay(1, (n) => {
console.log(n);
delay(2, (n) => {
console.log(n);
});
});
|
如果呼叫次非常多,程式碼就會變成這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
delay(1, (n) => {
console.log(n);
delay(2, (n) => {
console.log(n);
delay(3, (n) => {
console.log(n);
delay(4, (n) => {
console.log(n);
delay(5, (n) => {
console.log(n);
delay(6, (n) => {
console.log(n);
});
});
});
});
});
});
|
由上述可知,沒有進一步的非同步需求,回呼函式是直覺而簡單的,但如果需要執行多次非同步程式,就會不利於程式碼的閱讀和維護。
從 ES6 開始,JavaScript 引入了一些新功能,可以幫助我們不用回呼就能處理非同步程式:
- Promise 物件(ES6)
- Async/Await 語法(Promise 語法糖 ES8)