🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

現在42Tokyo正在進行最後的團隊課題(Web應用程式開發),雖然在工作中也經常用JavaScript的async/await來寫程式,但不禁懷疑為什麼要這樣寫?因此,我回顧了非同步處理的歷史。
※本文是從JS的角度寫的,請多多包涵。。。

JavaScript非同步處理的進化史:從回呼函數到async/await

在JavaScript中,如何處理耗時的操作(如API通訊或計時器等)始終是一個巨大挑戰。讓我們回顧這個演變的歷史,並與代碼的發展相結合。

歷史的演變

世代 手法 進化的重點
第1世代 回呼函數 所有的操作都需手動管理(容易出錯)
第2世代 Promise (ES6/2015) 能將非同步處理當作「物件」來處理,並實現標準化
第3世代 async/await (ES2017) 基於Promise,進一步使「外觀」接近同步處理的完成形態

1. 回呼函數時代(黎明期)

在早期的JavaScript中,傳遞一個「完成後要執行的函數(回呼函數)」作為參數,是唯一的方法。

特徵

  • 調用函數時,必須將之後的計劃一起交付。
  • 當處理連續時,會出現不斷向右偏移的「回呼地獄」。

缺乏錯誤處理

  • 重複性:必須多次撰寫failureCallback,導致代碼冗長。
  • 遺漏:若某個地方忘記傳遞failureCallback,則在那裡發生的錯誤不會引發任何反應(被隱藏)。
  • 可讀性:因為不斷向右偏移,會不清楚哪些地方是哪些處理的結尾。
 // 深入的嵌套
getData(function(a) {
    getMoreData(a, function(b) {
        getEvenMoreData(b, function(c) {
            console.log(c);
        }, failureCallback);
    }, failureCallback);
}, failureCallback);

2. Promise的出現(2015年 / ES6)

為了解決回呼函數的缺點,「承諾未來結果的物件」Promise出現了。

特徵

  • 狀態管理:擁有Pending(等待中)、Fulfilled(成功)、Rejected(失敗)三種狀態。
  • 方法鏈:可以使用.then()將處理串接。
  • 錯誤的統一管理:最後只需要寫.catch(),就能捕捉到過程中發生的任何錯誤。
 // 垂直流動的「鏈式」結構
getData()
    .then(a => getMoreData(a))
    .then(b => getEvenMoreData(b))
    .then(c => console.log(c))
    .catch(error => console.error(error)); // ← 這一行可以捕捉所有步驟的失敗!
  • 統一管理:一旦成功,就不會再變成失敗,因此無論在哪步失敗,最後的.catch都能捕捉到。
  • 扁平化:只需連接then(),就能將非同步處理連結成一個像管道一樣的流程。與回呼函數不同,不會變成階梯式,而是可以上下閱讀。

3. async/await的登場 (ES2017)

  • async/await並不是JavaScript獨創的,而是仿照2012年C# 5.0引入的概念。
  • 基於Promise,並讓你能用與「同步處理(直線向下執行的普通代碼)」相同的外觀來書寫。
  • 「成功之後到下一步,失敗則進入catch」的流程,可以像普通的if語句或錯誤處理一樣自然地書寫。
比較項目 C# JavaScript
返回值類型 TaskTask<T> Promise
關鍵字 async / await async / await
錯誤處理 可以使用 try-catch 可以使用 try-catch

這兩種語言的編譯器或引擎在背景中創建狀態機(State Machine),以控制程式的暫停和繼續,這樣就可以在不阻塞線程(在JS的情況下是主線程)的情況下進行等待。

特徵

  • async:宣告為非同步函數。
  • await:在Promise返回結果之前於該行等待(同時不會阻塞執行線程)。
  • 極致的可讀性:可以在不意識到其非同步性情況下編寫代碼。
async function executeTasks() {
    try {
        const result = await doSomething();
        const newResult = await doSomethingElse(result);
        const finalResult = await doThirdThing(newResult);
        console.log(`最終結果: ${finalResult}`);
    } catch (error) {
        // 在任何 await 失敗時都會到這裡
        failureCallback(error);
    }
}

如今,除非有特殊原因,否則不會再直接撰寫回呼函數,而是「以async/await的方式智能地調用返回Promise的函數」成為了標準做法。

總結:為何會進化?

時代 手法 主要解決方案
~2014年 回呼函數 非同步處理的基礎,但嵌套太深,難以控制。
2015年~ Promise Promise明確解決了問題,能作為物件處理結果。
2017年~ async/await 解決了「可讀性」問題,可以像同步處理一樣書寫。

老實說,在我進行調查之前,我並沒有深入思考非同步處理為何要用async/await來寫,但調查之後,我理解了回呼地獄的原因以及為什麼async/await是更優越的選擇!
此外,我也希望能多了解Promise物件,等到有內容整理好後,我會寫一篇相關的文章!
非常感謝您閱讀到最後!💪

參考資料
MDN - Promise的使用
MDN - async function


原文出處:https://qiita.com/MoriP-K/items/c9590934c7e72638107b


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝18   💬8   ❤️1
485
🥈
我愛JS
📝1   💬6  
41
🥉
💬1  
4
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付