JS 筆記 | callback, promise, async/await 的前世今生

更新 發佈閱讀 6 分鐘

非同步的概念在 JavaScript 中一直是一大命題,通常用來執行那些可能很耗時的工作,比如打 API。所以所謂非同步用的好,網頁順暢沒煩惱。

而 JavaScript 中執行非同步的方式從最早的 callback function 已經推進到 promise,再到現在簡潔的 async/await,不只實務上常用,也已經變成幾乎必考的面試或技術題。

Callback Function

常會看到文章寫到 callback 都會提到 callback 就是 “把一個 B 函式做為參數傳進另一個 A 函式,透過 A 函式來呼叫它”。

這樣做的原因有兩個:

  1. 讓 B 函式滿足某個條件才 “被動地” 去執行。
  2. 讓函式之間有執行的順序。

白話一點就是我要做完 A 才去執行 B。所以舉個例子,我要數字相加的結果如果有大於 5 (A函式) 時,去對相加後的結果乘以 10 (B函式),可以這樣寫:

const addNum = (a, b, callback) =>{
const plusNum = a + b
if(plusNum > 5){
callback(plusNum)
}else{
console.log('the plusNum < 5')
}
}

const multiNum = (num) => console.log(num * 10)

addNum(6, 2, multiNum)



Promise

試想一下,剛剛的例子僅是 A 執行完去執行 B,但如果今天 B 執行完還要執行 C、D、E…呢?恐怕一不小心就迷失在茫茫的 callback 大海中了。

萬幸,promise 的從天而降讓我們不需要一層包一層的程式碼,可以更容易看到函式間的次序。

Promise 的使用上會先建立一個 promise 物件,而 promise 通常會有三種狀態:

  1. Pending:表示正在處理中。
  2. Resolve:表示處理成功。
  3. Reject:表示處理失敗。

我們通常靠著 then 來接收成果的結果並繼續處理下一件事,然後用 catch 來捕獲失敗的狀況。

所以 promise 的語法架構大概是像這樣:A().then(B()).catch(err)

把前一段的程式碼改成 promise 會像這樣:

const addNum = (a, b)=>{
return new Promise((resolve, reject)=>{
const plusNum = a + b
if(plusNum > 5){
resolve(plusNum)
}else{
reject('reject!')
}
})
}

addNum(6, 2)
.then(num => multiNum(num))
.catch(err => console.error(err))



Async/await

這是基於 promise 的語法糖,他一切的運作原理都還是建立在 promise 上,只是 async/await 的寫法讓我們可以用看似同步的語法來撰寫非同步的內容。

我們會再函式一開始用 async 定義一個非同步函式,並在內部使用 await 等待 promise 執行完畢 (A函式),然後再去執行下一個動作 (B函式)。

沒錯,我們還是需要用到 promise,就像一開始講的,async/await 只是 promise 的語法糖,讓我們不用 then 下去。

所以如果要把前述的 promise 寫法改成 async/await,我們還是得保留 promise 的建立。

而在 async/await 中,我們依靠 try 來處理 promise 操作成功的情況,然後靠 catch 捕獲錯誤結果。

const calculate = async(a, b) =>{
try{
const plusNum = await addNum(a, b)
multiNum(plusNum)
}
catch(err){
console.error(err)
}
}

calculate(6, 2)

會有一個問題,就是 B 函式multiNum(plusNum)有沒有需要加上 await?答案是加了可以動,但沒必要加。

像前述講的,await 在等待的是一個 promise 非同步函式的操作完成,但我們的multiNum(plusNum)實際是個同步函式,為了不必要的誤會 (不論是人的誤會還是程式碼執行的誤會),盡量不要在同步函式前添加 await



[補充] 為什麼要用 async/await 不用 promise

在會議上前輩問了一個問題:為什麼現在推崇使用 async/await?

我說:因為可以把非同步用同步的語法寫出...?

很顯然,我了解得不夠透徹,所以開始了同步與非同步小課堂~

大家都知道 JavaScript 的程式碼執行是一行接著一行這樣執行下來的嘛,所謂單執行序語言,遇到非同步的事件時會變成多執行序,比方說 setTimeout 被丟到 web API 中執行,promiseasync/await 的差異就在這裡。

當我們的程式碼執行到 promise 時,其實是進入多執行序的,ok,這滿合理的,因為 promise 是個非同步事件。但這樣有個問題,假設今天 promise 是打一組 API 做資料請求,在他後面有一串 code,比如是點擊按鈕渲染取得的資料內容,我們勢必得寫一組 code 去手動監聽剛剛的 API 請求到底結束沒、有沒有資料回來。

async/await 不一樣,當今天整段 code 運行中遇到 async function,在 async function 後面的 code 會整組停下來,等待 async function 中 await 的非同步事件處理完成。對應剛剛的情況,當我們使用 async/await 做 API 請求,我們是不用手動監聽的,因為當 async function 後面的程式碼能開始執行時,就表示 API 請求已經結束了。

這是今天了解到得額外知識。



參考資料

  1. callback, promise, async/await 使用方式教學以及介紹 Part I
  2. 我要學會 JS(三):callback、Promise 和 async/await 那些事兒
留言
avatar-img
留言分享你的想法!
avatar-img
Jeremy Ho的沙龍
20會員
37內容數
這個專題用來存放我在學習網頁開發時的心得及知識。
Jeremy Ho的沙龍的其他內容
2023/11/17
2023/11/17
2023/11/16
TypeScript 基礎語法
Thumbnail
2023/11/16
TypeScript 基礎語法
Thumbnail
2023/10/09
String 基本操作方法
Thumbnail
2023/10/09
String 基本操作方法
Thumbnail
看更多
你可能也想看
Thumbnail
簡要說明 JavaScript 的 Event Loop JavaScript 是單執行緒 (single-threaded) 語言,這意味著它一次只能執行一件事,因此所有函式都需要排隊等待執行,這被稱為同步 (synchronous)。在同步操作中,若函式過多或過於複雜,會導致程式阻塞 (blo
Thumbnail
簡要說明 JavaScript 的 Event Loop JavaScript 是單執行緒 (single-threaded) 語言,這意味著它一次只能執行一件事,因此所有函式都需要排隊等待執行,這被稱為同步 (synchronous)。在同步操作中,若函式過多或過於複雜,會導致程式阻塞 (blo
Thumbnail
非同步程式設計(Asynchronous programming) 或是簡單的稱之為 async,它是一種並發程式模型(concurrent programming model),其目的就是讓多個任務能同時在作業系統的執行緒上執行,並透過 async/.await 保留同步。
Thumbnail
非同步程式設計(Asynchronous programming) 或是簡單的稱之為 async,它是一種並發程式模型(concurrent programming model),其目的就是讓多個任務能同時在作業系統的執行緒上執行,並透過 async/.await 保留同步。
Thumbnail
AJAX(Asynchronous JavaScript And XML)為JavaScript和XML的非同步技術,可以讓瀏覽器建立快速、更加和容易使用的操作介面。AJAX核心技術是非同步的HTTP請求(Asynchronous HTTP Requests),除了第一次載入網頁外,HTTP請求是在
Thumbnail
AJAX(Asynchronous JavaScript And XML)為JavaScript和XML的非同步技術,可以讓瀏覽器建立快速、更加和容易使用的操作介面。AJAX核心技術是非同步的HTTP請求(Asynchronous HTTP Requests),除了第一次載入網頁外,HTTP請求是在
Thumbnail
歡迎來到 React 白話文運動第三篇,今天我們將深入探討 JavaScript 中的非同步操作,並介紹兩個重要的關鍵字:Async 與 Await。在這篇文章中,我們將透過實例演示非同步操作的概念,以及如何使用 Promise、Fetch、Async 和 Await 來更有效地處理非同步程式。
Thumbnail
歡迎來到 React 白話文運動第三篇,今天我們將深入探討 JavaScript 中的非同步操作,並介紹兩個重要的關鍵字:Async 與 Await。在這篇文章中,我們將透過實例演示非同步操作的概念,以及如何使用 Promise、Fetch、Async 和 Await 來更有效地處理非同步程式。
Thumbnail
執行以上程式碼,然後看到了這個結果: 為什麼「延遲0秒」的函式寫在上方,但在console印出的結果,它還是被排在第二順位? 利用AC教材提供的youtube演講連結一窺究竟: 演講提供了更多JS的細節概念,身為JS新手的我還在消化,但若針對教案提出的問題來回答,加上利用google大神查到MDN的
Thumbnail
執行以上程式碼,然後看到了這個結果: 為什麼「延遲0秒」的函式寫在上方,但在console印出的結果,它還是被排在第二順位? 利用AC教材提供的youtube演講連結一窺究竟: 演講提供了更多JS的細節概念,身為JS新手的我還在消化,但若針對教案提出的問題來回答,加上利用google大神查到MDN的
Thumbnail
前言 這是第一次寫技術文章,但其實應該也只能說是蒐集很多資料並學習如何透過自己的話解釋的內容,並不能像其他大神可能分享一些很酷的技術,目標就單純是為了完成最後一週的作業(如下)。 走入非同步之前 執行環境(Execution Context) 執行環境堆疊 (Execution stack)
Thumbnail
前言 這是第一次寫技術文章,但其實應該也只能說是蒐集很多資料並學習如何透過自己的話解釋的內容,並不能像其他大神可能分享一些很酷的技術,目標就單純是為了完成最後一週的作業(如下)。 走入非同步之前 執行環境(Execution Context) 執行環境堆疊 (Execution stack)
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News