什麼是 Asynchronous (非同步)?

1
什麼是 Asynchronous (非同步)?:非同步(Asynchronous)是程式設計中不阻塞主執行緒、允許程式在等待 I/O 操作(如網路請求、檔案讀取、資料庫查詢)時繼續執行其他任務的程式模型。它透過回調、Promise 或 async/await 等機制實現,讓單執行緒語言也能高效處理並發,提升系統吞吐量與響應速度,特別適合 Web 開發與伺服器應用。

什麼是 Asynchronous (非同步)?

非同步(Asynchronous)是程式設計中不阻塞主執行緒、允許程式在等待 I/O 操作(如網路請求、檔案讀取、資料庫查詢)時繼續執行其他任務的程式模型。它透過回調、Promise 或 async/await 等機制實現,讓單執行緒語言也能高效處理並發,提升系統吞吐量與響應速度,特別適合 Web 開發與伺服器應用。

 

同步 vs 非同步的核心差異

同步程式像排隊買票,一人完成才能輪到下一個,遇到等待就全體停滯:

print("開始請求")
response = requests.get("https://api.example.com")  # 阻塞 2 秒
print("請求完成")  # 2 秒後才執行
# 總耗時:2 秒
 

非同步程式像多工處理,發起請求後立即做其他事,完成時通知:

print("開始請求")
response = await requests.get("https://api.example.com")  # 不阻塞
print("處理其他任務")
print("請求完成")  # 幾乎立即執行
# 總耗時:< 0.1 秒
 

非同步的實現機制

1. 回調函式(Callback)

最早方式,操作完成時呼叫指定函式:

// 舊式 Node.js
getUser(123, function(user) {
    console.log(user.name);  // 回調執行
});
console.log("繼續執行");  // 先執行

缺點:回調地獄(Callback Hell),巢狀過深難維護。

 

2. Promise

封裝非同步結果,支援鏈式處理:

getUser(123)
    .then(user => console.log(user.name))
    .catch(err => console.error(err));
 

3. async/await(現代標準)

語法糖,讓非同步程式碼像同步般可讀:

async function fetchUser() {
    try {
        const user = await getUser(123);  // 暫停等待
        console.log(user.name);
    } catch (err) {
        console.error(err);
    }
}
 

語言別非同步實現

語言 機制 範例語法
JavaScript Event Loop + Promise async/awaitfetch()
Python asyncio async defawaitaiohttp
C# Task Parallel Library async Taskawait
Go Goroutines + Channel go func()<-ch
Java CompletableFuture supplyAsync().thenApply()

Python asyncio 範例

import asyncio
import aiohttp

async def fetch_user(session, user_id):
    async with session.get(f'https://api.example.com/users/{user_id}') as resp:
        return await resp.json()

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_user(session, i) for i in range(1, 6)]
        users = await asyncio.gather(*tasks)  # 並行執行
        print(users)

asyncio.run(main())
 

非同步的執行原理

事件迴圈(Event Loop)是核心,管理任務佇列:

1. 註冊非同步任務 → 事件迴圈佇列
2. 主執行緒執行其他程式碼
3. 任務完成 → 事件迴圈調度回調
4. 重複 1-3
 

JavaScript 事件迴圈簡圖

任務佇列: [setTimeout CB, fetch CB, UI render]

Event Loop ← 主執行緒
 

實際應用場景

1. Web API 伺服器

// Express 傳統(阻塞)
app.get('/users', async (req, res) => {
    const users = await db.query('SELECT * FROM users');  // 阻塞
    res.json(users);
});

// 非同步優化(Node.js)
app.get('/users', async (req, res) => {
    const usersTask = db.query('SELECT * FROM users');
    const statsTask = db.query('SELECT COUNT(*) FROM stats');
    const [users, stats] = await Promise.all([usersTask, statsTask]);
    res.json({ users, stats });  // 並行查詢,速度 x2
});

 

2. 前端使用者體驗

// 並行載入,提升首屏速度
const [user, posts, comments] = await Promise.all([
    fetchUser(),
    fetchPosts(),
    fetchComments()
]);

 

非同步的優點與挑戰

優點

  • 高吞吐量:單執行緒處理萬級並發

  • 低延遲:不阻塞 UI,主執行緒保持響應

  • 資源效率:避免執行緒切換開銷

挑戰

  • 複雜性:錯誤處理、狀態管理困難

  • 除錯困難:執行順序非線性

  • 資源洩漏:未取消請求浪費記憶體

 

最佳實務與陷阱避免

正確寫法

async function processUsers() {
    try {
        const users = await fetchUsers();
        return users.map(formatUser);
    } catch (error) {
        console.error('獲取使用者失敗:', error);
        return [];  // 優雅降級
    }
}
 

常見錯誤

// 忘記 await
const users = fetchUsers();  // Promise 未解析
console.log(users[0].name);  // undefined

// 未處理錯誤
fetchUsers().then(users => {
    // error 被吞沒
});
 

效能技巧

  • Promise.all() 並行執行

  • Promise.allSettled() 部分失敗仍繼續

  • 設定超時 Promise.race([task, timeout(5000)])

  • 取消機制 AbortController

非同步是現代 Web 開發的基石,從 Node.js 單執行緒神話到 Python asyncio,從前端 SPA 到微服務 API。掌握 async/await 與事件迴圈原理,就能打造高效、響應迅速的應用,成為全端開發的必備技能。