Node.js中的Async和Await函數解析
本文主要和大家介紹了Node.js中的Async和Await函數的相關知識,你將學習如何使用Node.js中的async函數(async/await)來簡化callback或Promise.非常不錯,具有參考借鏡價值,需要的朋友可以參考下,希望能幫助大家。
非同步語言結構在其他語言中已經存在了,像c#的async/await、Kotlin的coroutines、go的goroutines,隨著Node.js 8的發布,期待已久的async函數也在其中預設實現了。
Node中的async函數是什麼?
當函數宣告為一個Async函數它會傳回一個 AsyncFunction 對象,它們類似於 Generator 因為執可以被暫停。唯一的區別是它們返回的是 Promise 而不是 { value: any, done: Boolean } 物件。不過它們還是非常相似,你可以使用 co 套件來獲得相同的功能。
在async函式中,可以等待 Promise 完成或捕捉它拒絕的原因。
如果你要在Promise中實作一些自己的邏輯的話
function handler (req, res) { return request('https://user-handler-service') .catch((err) => { logger.error('Http error', err) error.logged = true throw err }) .then((response) => Mongo.findOne({ user: response.body.user })) .catch((err) => { !error.logged && logger.error('Mongo error', err) error.logged = true throw err }) .then((document) => executeLogic(req, res, document)) .catch((err) => { !error.logged && console.error(err) res.status(500).send() }) }
可以使用async/await 讓這個程式碼看起來像同步執行的程式碼
async function handler (req, res) { let response try { response = await request('https://user-handler-service') } catch (err) { logger.error('Http error', err) return res.status(500).send() } let document try { document = await Mongo.findOne({ user: response.body.user }) } catch (err) { logger.error('Mongo error', err) return res.status(500).send() } executeLogic(document, req, res) }
在舊的v8版本中,如果有個promise 的拒絕沒有被處理你會得到一個警告,可以不用建立一個拒絕錯誤監聽函數。然而,建議在這種情況下退出你的應用程式。因為當你不處理錯誤時,應用程式處於一個未知的狀態。
process.on('unhandledRejection', (err) => { console.error(err) process.exit(1) })
async函數模式
在處理非同步操作時,有許多例子讓他們就像處理同步程式碼一樣。如果使用 Promise 或 callbacks 來解決問題時需要使用很複雜的模式或外部函式庫。
當需要再循環中使用非同步取得資料或使用 if-else 條件時就是一種很複雜的情況。
指數回退機制
使用 Promise 實現回退邏輯相當笨拙
function requestWithRetry (url, retryCount) { if (retryCount) { return new Promise((resolve, reject) => { const timeout = Math.pow(2, retryCount) setTimeout(() => { console.log('Waiting', timeout, 'ms') _requestWithRetry(url, retryCount) .then(resolve) .catch(reject) }, timeout) }) } else { return _requestWithRetry(url, 0) } } function _requestWithRetry (url, retryCount) { return request(url, retryCount) .catch((err) => { if (err.statusCode && err.statusCode >= 500) { console.log('Retrying', err.message, retryCount) return requestWithRetry(url, ++retryCount) } throw err }) } requestWithRetry('http://localhost:3000') .then((res) => { console.log(res) }) .catch(err => { console.error(err) })
程式碼看的讓人很頭疼,你也不會想看這樣的程式碼。我們可以使用async/await重新這個例子,使其更簡單
function wait (timeout) { return new Promise((resolve) => { setTimeout(() => { resolve() }, timeout) }) } async function requestWithRetry (url) { const MAX_RETRIES = 10 for (let i = 0; i <= MAX_RETRIES; i++) { try { return await request(url) } catch (err) { const timeout = Math.pow(2, i) console.log('Waiting', timeout, 'ms') await wait(timeout) console.log('Retrying', err.message, i) } } }
上面程式碼看起來很舒服對不對
中間值
不像前面的例子那麼嚇人,如果你有3個非同步函數依序相互依賴的情況,那麼你必須從幾個難看的解決方案中進行選擇。
functionA 回傳一個 Promise ,那麼 functionB 需要這個值而 functioinC 需要 functionA 和 functionB 完成後的值。
方案1: then 聖誕樹
function executeAsyncTask () { return functionA() .then((valueA) => { return functionB(valueA) .then((valueB) => { return functionC(valueA, valueB) }) }) }
用這個解決方案,我們在第三個then 中可以獲得valueA 和valueB ,然後可以像前面兩個then 一樣得到valueA 和valueB 的值。這裡不能將聖誕樹(毀掉地獄)拉平,如果這樣做的話會丟失閉包, valueA 在 functioinC 中將不可用。
方案2:移到上一層作用域
function executeAsyncTask () { let valueA return functionA() .then((v) => { valueA = v return functionB(valueA) }) .then((valueB) => { return functionC(valueA, valueB) }) }
在這顆聖誕樹中,我們使用更高的作用域保變數valueA ,因為valueA 作用域在所有的then 作用域外面,所以functionC 可以拿到第一個functionA 完成的值。
這是一個很有效扁平化 .then 鏈"正確"的語法,然而,這種方法我們需要使用兩個變數 valueA 和 v 來保存相同的值。
方案3:使用一個多餘的陣列
function executeAsyncTask () { return functionA() .then(valueA => { return Promise.all([valueA, functionB(valueA)]) }) .then(([valueA, valueB]) => { return functionC(valueA, valueB) }) }
在函數 functionA 的 then 中使用一個陣列將 valueA 和 Promise 一起返回,這樣能有效的扁平化聖誕樹(回調地獄)。
方案4:寫一個幫助函數
const converge = (...promises) => (...args) => { let [head, ...tail] = promises if (tail.length) { return head(...args) .then((value) => converge(...tail)(...args.concat([value]))) } else { return head(...args) } } functionA(2) .then((valueA) => converge(functionB, functionC)(valueA))
這樣是可行的,寫一個幫助函數來屏蔽上下文變數宣告。但是這樣的程式碼非常不利於閱讀,對於不熟悉這些魔法的人來說就更難了。
使用async/await 我們的問題神奇般的消失
async function executeAsyncTask () { const valueA = await functionA() const valueB = await functionB(valueA) return function3(valueA, valueB) }
使用async/await 處理多個平行請求
和上面一個差不多,如果你想一次執行多個非同步任務,然後在不同的地方使用它們的值可以使用async/await 輕鬆搞定。
async function executeParallelAsyncTasks () { const [ valueA, valueB, valueC ] = await Promise.all([ functionA(), functionB(), functionC() ]) doSomethingWith(valueA) doSomethingElseWith(valueB) doAnotherThingWith(valueC) }
陣列迭代方法
你可以在 map 、 filter 、 reduce 方法中使用async函數,雖然它們看起來不是很直觀,但是你可以在控制台中實驗以下程式碼。
1.map
function asyncThing (value) { return new Promise((resolve, reject) => { setTimeout(() => resolve(value), 100) }) } async function main () { return [1,2,3,4].map(async (value) => { const v = await asyncThing(value) return v * 2 }) } main() .then(v => console.log(v)) .catch(err => console.error(err))
2.filter
function asyncThing (value) { return new Promise((resolve, reject) => { setTimeout(() => resolve(value), 100) }) } async function main () { return [1,2,3,4].filter(async (value) => { const v = await asyncThing(value) return v % 2 === 0 }) } main() .then(v => console.log(v)) .catch(err => console.error(err))
3.reduce
function asyncThing (value) { return new Promise((resolve, reject) => { setTimeout(() => resolve(value), 100) }) } async function main () { return [1,2,3,4].reduce(async (acc, value) => { return await acc + await asyncThing(value) }, Promise.resolve(0)) } main() .then(v => console.log(v)) .catch(err => console.error(err))
解決方案:
[ Promise { <pending> }, Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ] [ 1, 2, 3, 4 ] 10
如果是map迭代資料你會看到回傳值為[ 2, 4, 6, 8 ] ,唯一的問題是每個值被AsyncFunction 函數包裹在了一個Promise 中
所以如果想要得到它們的值,需要將陣列傳遞給Promise.All() 來解開Promise 的包裹。
main() .then(v => Promise.all(v)) .then(v => console.log(v)) .catch(err => console.error(err)) 一开始你会等待 Promise 解决,然后使用map遍历每个值 function main () { return Promise.all([1,2,3,4].map((value) => asyncThing(value))) } main() .then(values => values.map((value) => value * 2)) .then(v => console.log(v)) .catch(err => console.error(err))
這樣好像比較簡單?
如果在你的迭代器中如果你有一個長時間運行的同步邏輯和另一個長時間運行的非同步任務,async/await版本任然常有用
這種方式當你能拿到第一個值,就可以開始做一些計算,而不必等到所有Promise 完成才執行你的計算。儘管結果包裹在 Promise 中,但如果按順序執行結果會更快。
關於 filter 的問題
你可能发觉了,即使上面filter函数里面返回了 [ false, true, false, true ] , await asyncThing(value) 会返回一个 promise 那么你肯定会得到一个原始的值。你可以在return之前等待所有异步完成,在进行过滤。
Reducing很简单,有一点需要注意的就是需要将初始值包裹在 Promise.resolve 中
重写基于callback的node应用成
Async 函数默认返回一个 Promise ,所以你可以使用 Promises 来重写任何基于 callback 的函数,然后 await 等待他们执行完毕。在node中也可以使用 util.promisify 函数将基于回调的函数转换为基于 Promise 的函数
重写基于Promise的应用程序
要转换很简单, .then 将Promise执行流串了起来。现在你可以直接使用`async/await。
function asyncTask () { return functionA() .then((valueA) => functionB(valueA)) .then((valueB) => functionC(valueB)) .then((valueC) => functionD(valueC)) .catch((err) => logger.error(err)) }
转换后
async function asyncTask () { try { const valueA = await functionA() const valueB = await functionB(valueA) const valueC = await functionC(valueB) return await functionD(valueC) } catch (err) { logger.error(err) } } Rewriting Nod
使用 Async/Await 将很大程度上的使应用程序具有高可读性,降低应用程序的处理复杂度(如:错误捕获),如果你也使用 node v8+的版本不妨尝试一下,或许会有新的收获。
相关推荐:
以上是Node.js中的Async和Await函數解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

JavaScript是一種廣泛應用於Web開發的程式語言,而WebSocket則是一種用於即時通訊的網路協定。結合二者的強大功能,我們可以打造一個高效率的即時影像處理系統。本文將介紹如何利用JavaScript和WebSocket來實作這個系統,並提供具體的程式碼範例。首先,我們需要明確指出即時影像處理系統的需求和目標。假設我們有一個攝影機設備,可以擷取即時的影像數
