Home  >  Article  >  Web Front-end  >  Using Async and Await functions in Node.js

Using Async and Await functions in Node.js

亚连
亚连Original
2018-06-05 14:49:352427browse

This article mainly introduces the relevant knowledge of Async and Await functions in Node.js. It is very good and has reference value. Friends in need can refer to it

In this article, you will learn how to Use the async function (async/await) in Node.js to simplify callback or Promise.

Asynchronous language structures already exist in other languages, such as c#’s async/await, Kotlin’s coroutines, and go’s goroutines , with the release of Node.js 8, the long-awaited async function is also implemented by default.

What is the async function in Node?

When a function is declared as an Async function it returns an AsyncFunction object. They are similar to Generators in that execution can be paused. The only difference is that they return a Promise instead of a { value: any, done: Boolean } object. They are still very similar though, and you can use the co package to get the same functionality.

In an async function, you can wait for the Promise to complete or capture the reason for its rejection.

If you want to implement some of your own logic in 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()
 })
}

You can use async/await to make this code look like synchronously executed code

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)
}

In the old In the v8 version, if there is a promise rejection that is not handled, you will get a warning and you do not need to create a rejection error listening function. However, it is recommended to exit your application in this case. Because when you don't handle errors, the application is in an unknown state.

process.on('unhandledRejection', (err) => { 
 console.error(err)
 process.exit(1)
})

async function pattern

When dealing with asynchronous operations, there are many examples of making them look like synchronous code. If you use Promise or callbacks to solve the problem, you need to use a very complex pattern or external library.

It is a very complicated situation when you need to use asynchronous acquisition of data in a loop or use if-else conditions.

Exponential rollback mechanism

Using Promise to implement rollback logic is quite clumsy

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)
 })

The code is very troublesome to read. , and you don’t want to see code like this. We can redo this example using async/await to make it simpler

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)
 }
 }
}

The above code looks very comfortable, right

Intermediate value

Not as scary as the previous example, if you have 3 async functions that depend on each other in turn, then you have to choose from several ugly solutions.

functionA returns a Promise, then functionB needs this value and functioinC needs the values ​​after functionA and functionB complete.

Option 1: then Christmas tree

function executeAsyncTask () {
 return functionA()
 .then((valueA) => {
  return functionB(valueA)
  .then((valueB) => {   
   return functionC(valueA, valueB)
  })
 })
}

Using this solution, we can get valueA and valueB in the third then, and then we can do the same as the previous two thens Get the values ​​of valueA and valueB. You can't flatten the Christmas tree (ruin hell) here, if you do you'll lose the closure and valueA won't be available in functioinC.

Option 2: Move to the upper level scope

function executeAsyncTask () {
 let valueA
 return functionA()
 .then((v) => {
  valueA = v
  return functionB(valueA)
 })
 .then((valueB) => {
  return functionC(valueA, valueB)
 })
}

In this Christmas tree, we use the higher scope retainer valueA because valueA scope Outside all then scopes, so functionC can get the value of the first functionA completion.

This is a very "correct" syntax for flattening the .then chain, however, with this approach we need to use two variables valueA and v to hold the same value.

Option 3: Use an extra array

function executeAsyncTask () {
 return functionA()
 .then(valueA => {
  return Promise.all([valueA, functionB(valueA)])
 })
 .then(([valueA, valueB]) => {
  return functionC(valueA, valueB)
 })
}

Use an array in then of functionA to return valueA and Promise together, which can effectively flatten the Christmas tree (callback hell).

Option 4: Write a helper function

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))

This is feasible, write a helper function to shield the context variable declaration. But such code is very difficult to read, especially for people who are not familiar with these magics.

Using async/await our problems magically disappear

async function executeAsyncTask () {
 const valueA = await functionA()
 const valueB = await functionB(valueA)
 return function3(valueA, valueB)
}

Use async/await to handle multiple parallel requests

It’s similar to the above one, if you want to execute multiple requests at once Asynchronous tasks, and then using their values ​​in different places can be easily done using async/await.

async function executeParallelAsyncTasks () {
 const [ valueA, valueB, valueC ] = await Promise.all([ functionA(), functionB(), functionC() ])
 doSomethingWith(valueA)
 doSomethingElseWith(valueB)
 doAnotherThingWith(valueC)
}

Array iteration method

You can use async functions in map, filter, and reduce methods, although they may not seem very intuitive. But you can experiment with the following code in the console.

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))

Solution:

[ Promise {  }, Promise {  }, Promise {  }, Promise {  } ]
[ 1, 2, 3, 4 ]
10

If it is map iteration data, you will see that the return value is [2, 4, 6, 8]. The only problem is that each value is wrapped in a Promise by the AsyncFunction function

So if you want to get their values, you need to pass the array to Promise.All() to unwrap the 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))

This seems simpler?

The async/await version is still useful if you have a long-running synchronous logic and another long-running asynchronous task in your iterator

这种方式当你能拿到第一个值,就可以开始做一些计算,而不必等到所有 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+的版本不妨尝试一下,或许会有新的收获。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

如何获取Vue中的this.$router.push参数

在angularJs-$http中如何实现百度搜索时的动态下拉框

在angularjs数组中如何判断是否含有某个元素

The above is the detailed content of Using Async and Await functions in Node.js. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn