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 那些事兒
18會員
37內容數
這個專題用來存放我在學習網頁開發時的心得及知識。
留言0
查看全部
發表第一個留言支持創作者!
Jeremy Ho的沙龍 的其他內容
JavaScript Object 基礎操作筆記
JavaScript Array 基本操作筆記
JavaScript event loop / asynchronous.
JavaScript Object 基礎操作筆記
JavaScript Array 基本操作筆記
JavaScript event loop / asynchronous.
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
在談到閉包前,要先談談範圍鏈,在 ES6 以前,變數透過 var 宣告,當時切分變數的最小單位為「函式」,有以下特性需要留意:內層可以取得外層的變數,但外層無法取得內層變數,在「定義」函式時就決定了範圍鏈,而非執行時
誠然,財務數據能夠直觀反映一個企業過去的經營成效,但有時候單純的財務數字也可能產生完全相反的結論,誤導受眾。市場投資而言,還是應該通過分析和理解其背後的原因和趨勢來進行價值判斷。   比如說,JS環球生活可能就面臨這樣的“反差”。   從核心數據上看,2023年上半年,JS環球生活持續經營業
Thumbnail
在前端開發中,因為瀏覽器可以迅速啟動與關閉的特性,再加上 JavaScript 的 Garbage Collection 垃圾回收機制,常常讓前端開發者忽略了 JavaScript 的記憶體管理機制與 Memory Leak 帶來的危險性,有時應用的效能瓶頸可能就因此產生了。
Thumbnail
提升(Hoisting) 指的是在創造環境階段時就把變數準備好,這時值還沒被賦予到變數上。此類型的概念可以使用執行環境的「創造階段」與「執行階段」來理解。
Thumbnail
這兩年都用 Elixir + Phoenix + LiveView 在開發網站,遠離雜亂的 js 生態圈。最近剛好因為有相關需求,所以就大概看一下有哪些 js library 跟 framework,有興趣的就大概摸一下,寫個粗淺的心得
選1)當初為什麼報名這堂課? 目前是資訊員在職即將滿七年了, 從當初入職完全不熟悉工作內容, 網管、設備維護要做些什麼, 不會的就東學西學, 學著跟廠商怎麼處理, 也這樣堅持過來。 我雖然是讀資訊領域相關, 但不敢說自己是工程師(所以我自稱為資訊員, 覺得自己都是會點皮毛談不上專業),
過去我嘗試學習 JS 蠻多次,常常無疾而終,因為窮所以都是看網路文章、線上課程為主,會遇到的問題有: 在職學習的惰性,只要開始加班我就忘記學習 :D 寫得太少又急著看完課程,看完後就忘光了 :DD 便宜的 Udemy 課程大都是英文,聽不懂的語言+聽不懂的邏輯,放空 :DDD 六角直播班帶給我最大的
Thumbnail
參加六角學院的 JS 工程師養成直播班,課程由洧杰老師擔任授課老師,總共有九週的線上直播課程以及課前知識補充的學習影片,與大家分享此次直播班的簡單心得
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
在談到閉包前,要先談談範圍鏈,在 ES6 以前,變數透過 var 宣告,當時切分變數的最小單位為「函式」,有以下特性需要留意:內層可以取得外層的變數,但外層無法取得內層變數,在「定義」函式時就決定了範圍鏈,而非執行時
誠然,財務數據能夠直觀反映一個企業過去的經營成效,但有時候單純的財務數字也可能產生完全相反的結論,誤導受眾。市場投資而言,還是應該通過分析和理解其背後的原因和趨勢來進行價值判斷。   比如說,JS環球生活可能就面臨這樣的“反差”。   從核心數據上看,2023年上半年,JS環球生活持續經營業
Thumbnail
在前端開發中,因為瀏覽器可以迅速啟動與關閉的特性,再加上 JavaScript 的 Garbage Collection 垃圾回收機制,常常讓前端開發者忽略了 JavaScript 的記憶體管理機制與 Memory Leak 帶來的危險性,有時應用的效能瓶頸可能就因此產生了。
Thumbnail
提升(Hoisting) 指的是在創造環境階段時就把變數準備好,這時值還沒被賦予到變數上。此類型的概念可以使用執行環境的「創造階段」與「執行階段」來理解。
Thumbnail
這兩年都用 Elixir + Phoenix + LiveView 在開發網站,遠離雜亂的 js 生態圈。最近剛好因為有相關需求,所以就大概看一下有哪些 js library 跟 framework,有興趣的就大概摸一下,寫個粗淺的心得
選1)當初為什麼報名這堂課? 目前是資訊員在職即將滿七年了, 從當初入職完全不熟悉工作內容, 網管、設備維護要做些什麼, 不會的就東學西學, 學著跟廠商怎麼處理, 也這樣堅持過來。 我雖然是讀資訊領域相關, 但不敢說自己是工程師(所以我自稱為資訊員, 覺得自己都是會點皮毛談不上專業),
過去我嘗試學習 JS 蠻多次,常常無疾而終,因為窮所以都是看網路文章、線上課程為主,會遇到的問題有: 在職學習的惰性,只要開始加班我就忘記學習 :D 寫得太少又急著看完課程,看完後就忘光了 :DD 便宜的 Udemy 課程大都是英文,聽不懂的語言+聽不懂的邏輯,放空 :DDD 六角直播班帶給我最大的
Thumbnail
參加六角學院的 JS 工程師養成直播班,課程由洧杰老師擔任授課老師,總共有九週的線上直播課程以及課前知識補充的學習影片,與大家分享此次直播班的簡單心得