JavaScript 是單執行緒 (single-threaded) 語言,這意味著它一次只能執行一件事,因此所有函式都需要排隊等待執行,這被稱為同步 (synchronous)。在同步操作中,若函式過多或過於複雜,會導致程式阻塞 (blocking),網頁卡住。
為了解決阻塞問題,JavaScript 引入了非同步 (asynchronous) 機制,允許程式碼並發 (concurrency) 運作。非同步操作避免了阻塞,使網頁更加順暢。非同步的運作涉及三個主要部分:Call stack、Web API 和 Callback queue。
Event loop 是個守衛,不斷檢查 Call stack 是否為空。如果 Call stack 為空,它會將 Callback queue 中的回調函式 (callback function) 移回 Call stack 中執行。
這樣,JavaScript 能在單執行緒的基礎上處理非同步任務,保持網頁的流暢性。
那我們來看一下例子
console.log(‘hi’);
setTimeout(function cb(){
console.log(‘there’);
}, 5000);
console.log(‘JSConfEU’)
產出結果順序將會是 :
'hi'
'JSConfEU'
'there'
步驟解析
console.log('hi')
這是一個同步操作,直接在主線程上執行,並立即輸出 "hi"setTimeout(function cb(){console.log('there')},5000)
:setTimeout 是一個非同步操作。它會安排一個計時器,並將回調函式 cb 註冊到 Web API 中,計時5000毫秒(5秒)。此時,計時器開始倒數計時,並將回調函式 cb 放到 Web API 中,等待計時結束。註冊計時器的操作是同步的,所以立即返回,並繼續執行接下來的程式碼。console.log('JSConfEU')
這是一個同步操作,直接在主線程上執行,並立即輸出 "JSConfEU"console.log('there')
,並輸出 "there"Callback 是一種將函式 B 作為參數傳遞給函式 A 的方式,透過函式 A 在適當的時候執行函式 B。這樣做的目的是控制函式執行的順序,通常在需要滿足特定條件後才執行函式 B。
function greet(name, callback) {
console.log("Hello, " + name);
callback();
}
function sayGoodbye() {
console.log("Goodbye!");
}
greet("Alice", sayGoodbye);
如果按照Callback剛剛這樣執行的話,會造成函式層層嵌套,而 Promise 解決了這個問題,使程式更易讀。
Promise 有三種狀態:
Promise 通常使用 .then()
來處理成功,.catch()
來處理失敗。
如果把剛剛callback寫成promise的話,會像這樣:
function greet(name) {
return new Promise((resolve, reject) => {
if (!name) {
reject("錯誤");
} else {
console.log("Hello, " + name);
resolve();
}
});
}
function sayGoodbye() {
console.log("Goodbye!");
}
greet("Alice")
.then(() => {
sayGoodbye();
})
.catch(error => {
console.error("Error:", error);
});
可以更清晰地控制函式的執行順序。
async/await
是基於 Promise 的語法糖,使非同步程式碼看起來像同步執行。
這樣寫法更簡潔,不需要 .then
,錯誤處理用 try
和 catch
。
function greet(name) {
return new Promise((resolve, reject) => {
if(!name){
reject('錯誤')
} else {
console.log("Hello, " + name);
resolve();
}
});
}
function sayGoodbye() {
console.log("Goodbye!");
}
async function main() {
try {
await greet("Alice");
sayGoodbye();
} catch (error) {
console.error("Error:", error);
}
}
main();