2024-05-30|閱讀時間 ‧ 約 28 分鐘

非同步處理

    簡要說明 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 中,等待計時結束。註冊計時器的操作是同步的,所以立即返回,並繼續執行接下來的程式碼。


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


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



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

    分享至
    成為作者繼續創作的動力吧!
    © 2024 vocus All rights reserved.