非同步處理

更新於 發佈於 閱讀時間約 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
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
避免 thread 競速(Race Condition)是多執行緒編程中常見的挑戰之一。 Race Condition 發生在多個執行緒同時訪問和修改共享資源時,因為執行緒之間的執行順序無法預測,可能會導致數據的不一致性或意外行為。 本文主要介紹如何使用Lock來避免此狀況出現。 首先先看沒
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
JavaScript (簡稱 JS) 是具有一級函數的輕量級、直譯式或即時編譯的程式語言。它因為用作網頁的腳本語言而大為知名,但也用於許多非瀏覽器的環境,像是 Node.js 等。由於 JavaScript 語法上的一些缺點,軟體工程師們又設計出了 CoffeeScript、TypeScript 和
Thumbnail
本章節主要介紹了JavaScript中的流程控制,包括條件語句(如if、else if、else和三元運算子)和循環結構(如for迴圈、while迴圈等)。同時,也提供了如何使用break、continue和label來控制迴圈的執行。
※ 非同步概念總複習 為什麼要使用 Promise? 在 JavaScript 開發中,處理非同步操作是常見需求,涉及如文件讀寫、數據庫查詢或網路請求等耗時任務。傳統的回調方式可能導致代碼結構混亂,稱為「回調地獄」,難以維護和理解。 Promise 是解決這問題的方法。它是一個物件(objec
※ Promise基本介紹 什麼是 Promise? Promise 是 JavaScript 的一個構造函式,用於創建表示非同步操作的物件實例。使用 new Promise() 時,你會創建一個包含非同步操作的實例,這個實例可以透過其繼承的方法如 then(), catch(), 和 fina
※ 同步概念: 單純地「由上而下」執行程式碼,而且一次只執行一件事,也就是「按順序執行,一個動作結束才能切換到下一個」。缺點是你需要「等待」事情執行完畢,才能繼續往下走。 ※ 非同步概念: 盡可能讓主要的執行程序不需要停下來等待,若遇到要等待的事情,就發起一個「非同步處理」,讓主程序繼續執行,
※ 函式基礎介紹: ※ JavaScript 特殊的函式特性: 函式可以當成值來傳遞 (可以放進變數或放進物件) 函式可以當成函式的參數 callback - 在特定事件中觸發函式 (非同步特性) ※ 函式的基本寫法: ※ 調用 (invoke) 函式: "調用" 意指呼叫或執行
Thumbnail
非同步程式設計(Asynchronous programming) 或是簡單的稱之為 async,它是一種並發程式模型(concurrent programming model),其目的就是讓多個任務能同時在作業系統的執行緒上執行,並透過 async/.await 保留同步。
Thumbnail
為什麼需要非同步? 我們在「【Web微知識系列】 Web Workers」有介紹到在瀏覽器可執行腳本Javascript環境底下如何完成非同步的操作, 主要是為了讓任務更有效率的進行, 不會因為一個非常耗時的工作堵塞住整個服務, 導致無法服務他人的窘境。 大家應該經常在餐廳裡會看到服務員協
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
避免 thread 競速(Race Condition)是多執行緒編程中常見的挑戰之一。 Race Condition 發生在多個執行緒同時訪問和修改共享資源時,因為執行緒之間的執行順序無法預測,可能會導致數據的不一致性或意外行為。 本文主要介紹如何使用Lock來避免此狀況出現。 首先先看沒
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
JavaScript (簡稱 JS) 是具有一級函數的輕量級、直譯式或即時編譯的程式語言。它因為用作網頁的腳本語言而大為知名,但也用於許多非瀏覽器的環境,像是 Node.js 等。由於 JavaScript 語法上的一些缺點,軟體工程師們又設計出了 CoffeeScript、TypeScript 和
Thumbnail
本章節主要介紹了JavaScript中的流程控制,包括條件語句(如if、else if、else和三元運算子)和循環結構(如for迴圈、while迴圈等)。同時,也提供了如何使用break、continue和label來控制迴圈的執行。
※ 非同步概念總複習 為什麼要使用 Promise? 在 JavaScript 開發中,處理非同步操作是常見需求,涉及如文件讀寫、數據庫查詢或網路請求等耗時任務。傳統的回調方式可能導致代碼結構混亂,稱為「回調地獄」,難以維護和理解。 Promise 是解決這問題的方法。它是一個物件(objec
※ Promise基本介紹 什麼是 Promise? Promise 是 JavaScript 的一個構造函式,用於創建表示非同步操作的物件實例。使用 new Promise() 時,你會創建一個包含非同步操作的實例,這個實例可以透過其繼承的方法如 then(), catch(), 和 fina
※ 同步概念: 單純地「由上而下」執行程式碼,而且一次只執行一件事,也就是「按順序執行,一個動作結束才能切換到下一個」。缺點是你需要「等待」事情執行完畢,才能繼續往下走。 ※ 非同步概念: 盡可能讓主要的執行程序不需要停下來等待,若遇到要等待的事情,就發起一個「非同步處理」,讓主程序繼續執行,
※ 函式基礎介紹: ※ JavaScript 特殊的函式特性: 函式可以當成值來傳遞 (可以放進變數或放進物件) 函式可以當成函式的參數 callback - 在特定事件中觸發函式 (非同步特性) ※ 函式的基本寫法: ※ 調用 (invoke) 函式: "調用" 意指呼叫或執行
Thumbnail
非同步程式設計(Asynchronous programming) 或是簡單的稱之為 async,它是一種並發程式模型(concurrent programming model),其目的就是讓多個任務能同時在作業系統的執行緒上執行,並透過 async/.await 保留同步。
Thumbnail
為什麼需要非同步? 我們在「【Web微知識系列】 Web Workers」有介紹到在瀏覽器可執行腳本Javascript環境底下如何完成非同步的操作, 主要是為了讓任務更有效率的進行, 不會因為一個非常耗時的工作堵塞住整個服務, 導致無法服務他人的窘境。 大家應該經常在餐廳裡會看到服務員協