非同步處理

更新於 2024/05/31閱讀時間約 7 分鐘

簡要說明 JavaScript 的 Event Loop

JavaScript 是單執行緒 (single-threaded) 語言,這意味著它一次只能執行一件事,因此所有函式都需要排隊等待執行,這被稱為同步 (synchronous)。在同步操作中,若函式過多或過於複雜,會導致程式阻塞 (blocking),網頁卡住。

為了解決阻塞問題,JavaScript 引入了非同步 (asynchronous) 機制,允許程式碼並發 (concurrency) 運作。非同步操作避免了阻塞,使網頁更加順暢。非同步的運作涉及三個主要部分:Call stack、Web API 和 Callback queue。

  • Call stack:追蹤呼叫的函式,處理同步任務。
  • Web API:處理非同步任務(如 setTimeout),並將完成的任務送到 Callback queue。
  • Callback queue:存放已完成的非同步任務,等待重新進入 Call stack。

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’)

產出結果順序將會是 :

  1. 'hi'
  2. 'JSConfEU'
  3. 'there'


步驟解析

  • 執行 console.log('hi')這是一個同步操作,直接在主線程上執行,並立即輸出 "hi"
  • 執行 setTimeout(function cb(){console.log('there')},5000):setTimeout 是一個非同步操作。它會安排一個計時器,並將回調函式 cb 註冊到 Web API 中,計時5000毫秒(5秒)。此時,計時器開始倒數計時,並將回調函式 cb 放到 Web API 中,等待計時結束。註冊計時器的操作是同步的,所以立即返回,並繼續執行接下來的程式碼。
raw-image


  • 執行 console.log('JSConfEU')這是一個同步操作,直接在主線程上執行,並立即輸出 "JSConfEU"
raw-image


  • 5秒後,計時器完成,回調函式 cb 被移至 Callback queue 中:Event loop 檢查到 Call stack 為空,將回調函式 cb 移入 Call stack 中執行。執行 console.log('there'),並輸出 "there"
raw-image



Callback Function

Callback 是一種將函式 B 作為參數傳遞給函式 A 的方式,透過函式 A 在適當的時候執行函式 B。這樣做的目的是控制函式執行的順序,通常在需要滿足特定條件後才執行函式 B。

function greet(name, callback) {
console.log("Hello, " + name);
callback();
}

function sayGoodbye() {
console.log("Goodbye!");
}

greet("Alice", sayGoodbye);


  1. greet 函式:接受一個名字和一個回調函式,首先打印 "Hello, [名字]",然後執行回調函式。
  2. sayGoodbye 函式:只打印 "Goodbye!"
  3. 呼叫 greet:傳遞名字 "Alice"和回調函式 sayGoodbye,先打印 "Hello, Alice",接著打印 "Goodbye!"


Promise

如果按照Callback剛剛這樣執行的話,會造成函式層層嵌套,而 Promise 解決了這個問題,使程式更易讀。

Promise 有三種狀態:

  • Pending:進行中
  • Resolve:成功
  • Reject:失敗

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

async/await 是基於 Promise 的語法糖,使非同步程式碼看起來像同步執行。

  1. async:定義一個非同步函式。
  2. await:等待 Promise 完成,就像同步程式碼一樣。

這樣寫法更簡潔,不需要 .then,錯誤處理用 trycatch

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();

    avatar-img
    3會員
    17內容數
    留言0
    查看全部
    avatar-img
    發表第一個留言支持創作者!
    郭欣玫的沙龍 的其他內容
    xhr 在下面的例子裡,我們首先建立了一個 XMLHttpRequest 物件,並使用 .open() 開啟一個 URL,最後使用 .send() 發出 request。 具體來說步驟有四個: 建立XMLHttpReque 開啟一個請求。 送出請求。 拿到回應後去處理畫面要如何呈現。
    1.justify-content 用來控制主軸(水平)如何分配對齊 如果要改垂直對齊的話+上 flex-direction : column 2.align-item 他是控制子項目再交叉軸(垂直)的對齊方式 3.flex-shrink 收縮性! 當空間不足時預設會是1,等於是
    如果製作一個RGB色碼轉換器,算是考驗操作DOM! 那把它分成三個目標來依序完成, 抓slider的數字,存放在各個滑塊右邊的小格子 透過 slider.addEventListener("input", function (event) {...}) 監聽滑塊變動事件。 當事件被觸發時,取得
    以下展示三種置中方法 : <div class="method-container"> <div class="method-title">Flexbox</div> <div class="box"> <div class="dot"></div> </div> </di
    一般而言,組件之間的資料傳遞,可以使用 props 來達成,不過一旦層級過多的時候,props 就要逐層向下傳遞,會越來越麻煩且複雜。 而 provide、inject 可以解決這個問題,它可以提供一個「源頭」,子組件們可以藉由同一個源頭取得對應的資料,且沒有層級分別,都可以取得,就不用逐層傳遞資
    當 父組件 有數據想傳送到 子組件 就可以使用props 1​. 父層傳遞設置 可以在父組件的屬性給予一個值,當作要傳送到子組件的資料。 父層組件​ : <!-- App.vue (父組件) --> <template> <div> <ChildComponent greetin
    xhr 在下面的例子裡,我們首先建立了一個 XMLHttpRequest 物件,並使用 .open() 開啟一個 URL,最後使用 .send() 發出 request。 具體來說步驟有四個: 建立XMLHttpReque 開啟一個請求。 送出請求。 拿到回應後去處理畫面要如何呈現。
    1.justify-content 用來控制主軸(水平)如何分配對齊 如果要改垂直對齊的話+上 flex-direction : column 2.align-item 他是控制子項目再交叉軸(垂直)的對齊方式 3.flex-shrink 收縮性! 當空間不足時預設會是1,等於是
    如果製作一個RGB色碼轉換器,算是考驗操作DOM! 那把它分成三個目標來依序完成, 抓slider的數字,存放在各個滑塊右邊的小格子 透過 slider.addEventListener("input", function (event) {...}) 監聽滑塊變動事件。 當事件被觸發時,取得
    以下展示三種置中方法 : <div class="method-container"> <div class="method-title">Flexbox</div> <div class="box"> <div class="dot"></div> </div> </di
    一般而言,組件之間的資料傳遞,可以使用 props 來達成,不過一旦層級過多的時候,props 就要逐層向下傳遞,會越來越麻煩且複雜。 而 provide、inject 可以解決這個問題,它可以提供一個「源頭」,子組件們可以藉由同一個源頭取得對應的資料,且沒有層級分別,都可以取得,就不用逐層傳遞資
    當 父組件 有數據想傳送到 子組件 就可以使用props 1​. 父層傳遞設置 可以在父組件的屬性給予一個值,當作要傳送到子組件的資料。 父層組件​ : <!-- App.vue (父組件) --> <template> <div> <ChildComponent greetin
    你可能也想看
    Google News 追蹤
    Thumbnail
    *合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
    認識 async/await基本概念: async 的本質是 promise 的語法糖 ,只要 function 標記為 async,就表示裡頭可以撰寫 await 的同步語法,而 await 顧名思義就是「等待」,它會確保一個 promise 物件都解決 ( resolve ) 或出錯 ( re
    什麼是 Promise.all? 在有多個 Promise 的時候,使用 Promise.all 可以確保「所有的 Promise 都執行完以後,才進入 then」。 Promise.all 語法結構: Promise.all 接受的參數是陣列形式。 什麼時候要使用 Promise.all?
    ※ Promise基本介紹 什麼是 Promise? Promise 是 JavaScript 的一個構造函式,用於創建表示非同步操作的物件實例。使用 new Promise() 時,你會創建一個包含非同步操作的實例,這個實例可以透過其繼承的方法如 then(), catch(), 和 fina
    ※ 同步概念: 單純地「由上而下」執行程式碼,而且一次只執行一件事,也就是「按順序執行,一個動作結束才能切換到下一個」。缺點是你需要「等待」事情執行完畢,才能繼續往下走。 ※ 非同步概念: 盡可能讓主要的執行程序不需要停下來等待,若遇到要等待的事情,就發起一個「非同步處理」,讓主程序繼續執行,
    當前大多數網站不再是靜態網站,需要向後端的 Server 請求資料。而且,這些資料的請求通常是非同步進行的。在React中,目前被視為最佳實踐的方式是使用 useEffect。
    Thumbnail
    非同步程式設計(Asynchronous programming) 或是簡單的稱之為 async,它是一種並發程式模型(concurrent programming model),其目的就是讓多個任務能同時在作業系統的執行緒上執行,並透過 async/.await 保留同步。
    Thumbnail
    為什麼需要非同步? 我們在「【Web微知識系列】 Web Workers」有介紹到在瀏覽器可執行腳本Javascript環境底下如何完成非同步的操作, 主要是為了讓任務更有效率的進行, 不會因為一個非常耗時的工作堵塞住整個服務, 導致無法服務他人的窘境。 大家應該經常在餐廳裡會看到服務員協
    Thumbnail
    近期,有個有點冷門的消息是澳洲企業軟體巨頭、擁有 Jira、Trello 等協作工具的 Atlassian 宣佈,以 9.75 億美元收購超過 2500 萬客戶、非同步影片通訊服務的 Loom。 會注意到 Loom,是因為之前在逛 Readwise 官網時,看到了他們直接錄一段教學影片,放在文章中
    Thumbnail
    介紹 非同步程式設計模式(Asynchronous Programming Patterns) APM 非同步程式設計模型 EAP 事件架構非同步模式 TAP 以工作為基礎的非同步模式 先在主要以TAP APM (Asynchronous Programming Model) 非同步程式設計模型.N
    Thumbnail
    *合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
    認識 async/await基本概念: async 的本質是 promise 的語法糖 ,只要 function 標記為 async,就表示裡頭可以撰寫 await 的同步語法,而 await 顧名思義就是「等待」,它會確保一個 promise 物件都解決 ( resolve ) 或出錯 ( re
    什麼是 Promise.all? 在有多個 Promise 的時候,使用 Promise.all 可以確保「所有的 Promise 都執行完以後,才進入 then」。 Promise.all 語法結構: Promise.all 接受的參數是陣列形式。 什麼時候要使用 Promise.all?
    ※ Promise基本介紹 什麼是 Promise? Promise 是 JavaScript 的一個構造函式,用於創建表示非同步操作的物件實例。使用 new Promise() 時,你會創建一個包含非同步操作的實例,這個實例可以透過其繼承的方法如 then(), catch(), 和 fina
    ※ 同步概念: 單純地「由上而下」執行程式碼,而且一次只執行一件事,也就是「按順序執行,一個動作結束才能切換到下一個」。缺點是你需要「等待」事情執行完畢,才能繼續往下走。 ※ 非同步概念: 盡可能讓主要的執行程序不需要停下來等待,若遇到要等待的事情,就發起一個「非同步處理」,讓主程序繼續執行,
    當前大多數網站不再是靜態網站,需要向後端的 Server 請求資料。而且,這些資料的請求通常是非同步進行的。在React中,目前被視為最佳實踐的方式是使用 useEffect。
    Thumbnail
    非同步程式設計(Asynchronous programming) 或是簡單的稱之為 async,它是一種並發程式模型(concurrent programming model),其目的就是讓多個任務能同時在作業系統的執行緒上執行,並透過 async/.await 保留同步。
    Thumbnail
    為什麼需要非同步? 我們在「【Web微知識系列】 Web Workers」有介紹到在瀏覽器可執行腳本Javascript環境底下如何完成非同步的操作, 主要是為了讓任務更有效率的進行, 不會因為一個非常耗時的工作堵塞住整個服務, 導致無法服務他人的窘境。 大家應該經常在餐廳裡會看到服務員協
    Thumbnail
    近期,有個有點冷門的消息是澳洲企業軟體巨頭、擁有 Jira、Trello 等協作工具的 Atlassian 宣佈,以 9.75 億美元收購超過 2500 萬客戶、非同步影片通訊服務的 Loom。 會注意到 Loom,是因為之前在逛 Readwise 官網時,看到了他們直接錄一段教學影片,放在文章中
    Thumbnail
    介紹 非同步程式設計模式(Asynchronous Programming Patterns) APM 非同步程式設計模型 EAP 事件架構非同步模式 TAP 以工作為基礎的非同步模式 先在主要以TAP APM (Asynchronous Programming Model) 非同步程式設計模型.N