Async與Await函數(shù)怎么在Node.js中使用?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
我們提供的服務(wù)有:網(wǎng)站設(shè)計、做網(wǎng)站、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、墊江ssl等。為上1000家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的墊江網(wǎng)站制作公司
Node中的async函數(shù)是什么?
當(dāng)函數(shù)聲明為一個Async函數(shù)它會返回一個 AsyncFunction 對象,它們類似于 Generator 因為執(zhí)可以被暫停。唯一的區(qū)別是它們返回的是 Promise 而不是 { value: any, done: Boolean } 對象。不過它們還是非常相似,你可以使用 co 包來獲取同樣的功能。
在async函數(shù)中,可以等待 Promise 完成或捕獲它拒絕的原因。
如果你要在Promise中實現(xiàn)一些自己的邏輯的話
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 讓這個代碼看起來像同步執(zhí)行的代碼
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 的拒絕沒有被處理你會得到一個警告,可以不用創(chuàng)建一個拒絕錯誤監(jiān)聽函數(shù)。然而,建議在這種情況下退出你的應(yīng)用程序。因為當(dāng)你不處理錯誤時,應(yīng)用程序處于一個未知的狀態(tài)。
process.on('unhandledRejection', (err) => { console.error(err) process.exit(1) })
async函數(shù)模式
在處理異步操作時,有很多例子讓他們就像處理同步代碼一樣。如果使用 Promise 或 callbacks 來解決問題時需要使用很復(fù)雜的模式或者外部庫。
當(dāng)需要再循環(huán)中使用異步獲取數(shù)據(jù)或使用 if-else 條件時就是一種很復(fù)雜的情況。
指數(shù)回退機制
使用 Promise 實現(xiàn)回退邏輯相當(dāng)笨拙
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個異步函數(shù)依次相互依賴的情況,那么你必須從幾個難看的解決方案中進(jìn)行選擇。
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:使用一個多余的數(shù)組
function executeAsyncTask () { return functionA() .then(valueA => { return Promise.all([valueA, functionB(valueA)]) }) .then(([valueA, valueB]) => { return functionC(valueA, valueB) }) }
在函數(shù) functionA 的 then 中使用一個數(shù)組將 valueA 和 Promise 一起返回,這樣能有效的扁平化圣誕樹(回調(diào)地獄)。
方案4:寫一個幫助函數(shù)
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))
這樣是可行的,寫一個幫助函數(shù)來屏蔽上下文變量聲明。但是這樣的代碼非常不利于閱讀,對于不熟悉這些魔法的人就更難了。
使用 async/await 我們的問題神奇般的消失
async function executeAsyncTask () { const valueA = await functionA() const valueB = await functionB(valueA) return function3(valueA, valueB) }
使用 async/await 處理多個平行請求
和上面一個差不多,如果你想一次執(zhí)行多個異步任務(wù),然后在不同的地方使用它們的值可以使用 async/await 輕松搞定。
async function executeParallelAsyncTasks () { const [ valueA, valueB, valueC ] = await Promise.all([ functionA(), functionB(), functionC() ]) doSomethingWith(valueA) doSomethingElseWith(valueB) doAnotherThingWith(valueC) }
數(shù)組迭代方法
你可以在 map 、 filter 、 reduce 方法中使用async函數(shù),雖然它們看起來不是很直觀,但是你可以在控制臺中實驗以下代碼。
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迭代數(shù)據(jù)你會看到返回值為 [ 2, 4, 6, 8 ] ,唯一的問題是每個值被 AsyncFunction 函數(shù)包裹在了一個 Promise 中
所以如果想要獲得它們的值,需要將數(shù)組傳遞給 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))
這樣好像更簡單一些?
如果在你的迭代器中如果你有一個長時間運行的同步邏輯和另一個長時間運行的異步任務(wù),async/await版本任然常有用
這種方式當(dāng)你能拿到第一個值,就可以開始做一些計算,而不必等到所有 Promise 完成才運行你的計算。盡管結(jié)果包裹在 Promise 中,但是如果按順序執(zhí)行結(jié)果會更快。
關(guān)于 filter 的問題
你可能發(fā)覺了,即使上面filter函數(shù)里面返回了 [ false, true, false, true ] , await asyncThing(value) 會返回一個 promise 那么你肯定會得到一個原始的值。你可以在return之前等待所有異步完成,在進(jìn)行過濾。
Reducing很簡單,有一點需要注意的就是需要將初始值包裹在 Promise.resolve 中
重寫基于callback的node應(yīng)用成
Async 函數(shù)默認(rèn)返回一個 Promise ,所以你可以使用 Promises 來重寫任何基于 callback 的函數(shù),然后 await 等待他們執(zhí)行完畢。在node中也可以使用 util.promisify 函數(shù)將基于回調(diào)的函數(shù)轉(zhuǎn)換為基于 Promise 的函數(shù)
重寫基于Promise的應(yīng)用程序
要轉(zhuǎn)換很簡單, .then 將Promise執(zhí)行流串了起來。現(xiàn)在你可以直接使用`async/await。
function asyncTask () { return functionA() .then((valueA) => functionB(valueA)) .then((valueB) => functionC(valueB)) .then((valueC) => functionD(valueC)) .catch((err) => logger.error(err)) }
轉(zhuǎn)換后
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
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。
網(wǎng)站名稱:Async與Await函數(shù)怎么在Node.js中使用
網(wǎng)頁URL:http://muchs.cn/article40/ghsiho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器、App開發(fā)、自適應(yīng)網(wǎng)站、網(wǎng)站營銷、品牌網(wǎng)站設(shè)計、移動網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)