Details about the Async and Await functions in Node. js, node. jsasync

Source: Internet
Author: User
Tags findone

Details about the Async and Await functions in Node. js, node. jsasync

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

Asynchronous language structure already exists in other languages, such as c #'s async/await, Kotlin's coroutines, And go's goroutines, along with Node. with the release of 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, which is similar to Generator because the execution can be paused. The only difference is that they return Promise instead of {value: any, done: Boolean} objects. However, they are very similar. You can use the co package to obtain the same functions.

In the async function, you can wait for the Promise to complete or capture the reason for 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 the Code look like the code to be executed synchronously.

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 earlier v8 versions, if a promise rejection is not processed, you will receive a warning. You do not need to create a denial-of-error listener function. However, we recommend that you exit your application in this case. Because when you do not handle errors, the application is in an unknown state.

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

Async function mode

When processing asynchronous operations, there are many examples that let them be like processing Synchronous Code. If Promise or callbacks is used to solve the problem, a complicated mode or external library is required.

It is complicated to obtain data asynchronously or use the if-else condition in recycling.

Exponential rollback Mechanism

Using Promise to implement rollback logic is rather 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 a headache, and you don't want to read it. We can use async/await to repeat this example 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 comfortable, right?

Median

It's not as scary as in the previous example. If you have three asynchronous functions that depend on each other in turn, you have to choose from several ugly solutions.

FunctionA returns a Promise, so functionB needs this value, and functioinC needs the value after functionA and functionB are completed.

Solution 1: then Christmas tree

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

With this solution, we can obtain valueA and valueB in the third then, and then obtain the values of valueA and valueB in the same way as the two then above. Here we cannot flatten the Christmas tree (destroy the hell). If we do this, we will lose the closure and valueA will be unavailable in functioinC.

Solution 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 a higher scope variable valueA, because the valueA scope is out of all then scopes, so functionC can get the value completed by the first functionA.

This is a very effective flat. then chain "correct" syntax, however, we need to use two variables valueA and v to save the same value for this method.

Solution 3: Use a Redundant Array

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

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

Solution 4: Write a help 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 block the context variable declaration. But such code is very difficult to read, and it is more difficult for people who are not familiar with these magic.

Using async/await our problem disappears magically

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

Use async/await to process multiple parallel requests

Similar to the previous one, if you want to execute multiple asynchronous tasks at a time, and then use their values in different places, you can use async/await for ease.

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 do not seem intuitive, 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 { <pending> }, Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ][ 1, 2, 3, 4 ]10

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

Therefore, if you want to obtain their values, you need to pass the array to Promise. All () to unbind the Promise package.

Main (). then (v => Promise. all (v )). then (v => console. log (v )). catch (err => console. error (err) at the beginning, you will wait for Promise to solve the problem, and then use map to traverse each value 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 ))

Is it simpler?

If you have a long-running synchronization logic and another long-running asynchronous task in your iterator, The async/await version is often used.

In this way, when you get the first value, you can start to perform some calculations, instead of waiting until all Promise completes to run your calculations. Although the results are wrapped in Promise, the results are faster if executed in order.

Filter Problems

You may have noticed that even if the filter function returns [false, true, false, true], await asyncThing (value) if a promise is returned, you will surely get an original value. You can wait until all asynchronous operations are completed before return, and then filter.

Clustering is very simple. One thing to note is to wrap the initial values in Promise. resolve.

Rewrite the callback-based node application

By default, the Async function returns a Promise, so you can use Promises to rewrite any callback-based function, and await will wait for the execution to complete. In node, you can also use the util. promisify function to convert callback-based functions to Promise-based functions.

Override Promise-based applications

It is easy to convert. then Concatenates the Promise execution stream. Now you can use 'async'/await directly.

function asyncTask () { return functionA()  .then((valueA) => functionB(valueA))  .then((valueB) => functionC(valueB))  .then((valueC) => functionD(valueC))  .catch((err) => logger.error(err))}

After conversion

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

Using Async/Await will greatly make the application readable and reduce the processing complexity (for example, error capture) of the application ), if you also use node v8 +, try it and you may have a new harvest.

Summary

The above section describes the Async and Await functions in Node. js. I hope they will help you. If you have any questions, please leave a message and I will reply to you in a timely manner. Thank you very much for your support for the help House website!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.