国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

詳解Node.js中的Async和Await函數

2019-11-19 14:20:01
字體:
來源:轉載
供稿:網友

在本文中,你將學習如何使用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函數,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 新密市| 武穴市| 博野县| 富顺县| 屏东市| 蓬莱市| 石嘴山市| 龙口市| 老河口市| 遂昌县| 博兴县| 禄丰县| 武汉市| 临海市| 金华市| 科技| 偃师市| 和政县| 光泽县| 延吉市| 巴林右旗| 册亨县| 永修县| 达拉特旗| 鄱阳县| 烟台市| 晋城| 柘荣县| 北川| 金昌市| 芦山县| 怀化市| 麦盖提县| 绥江县| 竹山县| 东方市| 聂拉木县| 扶风县| 山东省| 舒兰市| 满洲里市|