Loop and asynchronous operation (promise / async / await)

Time:2020-11-18

preface

JS has a headache hell callback problem, so there is promise, chain calls, based on promise also appeared a variety of asynchronous solutions, one of the best personal feeling should be async / await solution, has always been vague use, until encountered loop and asynchronous problems, so spent time looking up information from 1 to 4

About promise

There are a lot of articles about promise on the Internet, and they are also very good

Basic concepts

Promise not only solves the problem of nested callbacks through chain calls, but also solves the problems of using return and throw in callback functions. The most important thing is its use in asynchronous solutions
Project has three states: pending / resumed / rejected, which can be obtained by changing the pending state, and it will not be changed once the state changes

Common API

  • Promise.all()//Multiple projects are executed in parallel. When all projects are reslove, reslove. When there is one project reject, reject
  • Promise.race()//And Promise.all Similarly, the difference is when the first promise reslove
  • Promise.then()//For chained calls
  • Promise.catch()//Capture reject

On asynchronous chestnuts

function sleep(dely) {
  return new Promise((reslove) => {
    setTimeout(() => {
      console.log(`dely ${dely}s`)
      reslove();
    }, dely * 1000);
  })
}

(async () => {
  console.log('before sleep')
  await sleep(2);
  console.log('after sleep')
})()

/** console
* before sleep
* dely 2s
* after sleep
*/

On async / await

This is an asynchronous solution in ES7. It has been natively supported in node v8.0.0 and above. It needs to be enabled in node version 8.0.0--harmony-async-awaitoption

How to use it

  • Async means that this is an async function. Await can only be used in this function.
  • Await means waiting for promise to return the result before continuing.
  • Await should be followed by a promise object
    Chestnut can refer to the previous chestnut, using async / await not only has clear semantics and simple operation, but also can catch errors synchronously

Loop and asynchronous operation

For example, there are the following settings: each person’s broken sleep time is a fixed value. If there are more than one person’s broken sleep time, then the latter person’s broken sleep time is the cumulative sum of the previous broken sleep time. Write a function to achieve the above description when passing in the name array, not to mention the code

Const users = {// defines 3 people, and each person's sleep time is 1
  leo: {
    name: 'leo',
    sleep: 1
  },
  mick: {
    name: 'mick',
    sleep: 1
  },
  jack: {
    name: 'jack',
    sleep: 1
  }
}

Function sleep (name, sleep) {// the function includes printing the current broken sleep time and accumulating the time of the person who has not broken sleep
  return new Promise((reslove) => {
    setTimeout(() => {
      console.log(`${name} sleep ${sleep}`);
      Object.keys(users).forEach(u => {
        if (name !== u) {
          users[u].sleep += sleep;
        }
      })
      reslove();
    }, sleep * 1000);
  })
}

This is what I wrote for the first time

(function consoleSleep(names) {
  return Promise.all(names.map(async n => {
    await sleep(users[n].name, users[n].sleep)
  }))
}(['leo', 'mick', 'jack']))
/** console
* leo sleep 1
* mick sleep 1
* jack sleep 1
*/

On the spot, I was very confused. I took the name array to loop, and then waited for various asynchronous operations during the current sleep in the loop. However, the result was very worrying. After analyzing the problem, I found that this was a problem of serial asynchronous operation,
okPromise.all()It’s parallel, but it’s getting rid of Promise.all After that, the result is still unchanged. After a variety of data search, it is found that the problem is map (laughter) (both map and foreach are parallel iterations). OK, change the code

(async function consoleSleep(names) {
  for(let i = 0; i < names.length; i++) {
    await sleep(users[names[i]].name, users[names[i]].sleep)
  }
}(['leo', 'mick', 'jack']))

/** console
* leo sleep 1
* mick sleep 2
* jack sleep 4
*/

Finally, the function is realized, and it is completed

Summary

Promise not only solves callback nesting, but also provides an asynchronous solution. You can use promise to encapsulate asynchronous operations, and then use async / await to make asynchronous operations synchronous. When using promise in a loop, you should pay attention to the different operation results brought by parallel and serial methods, Of course, other libraries also provide corresponding encapsulation methods (for example, Bluebird has mapseries, which can directly iterate and execute multiple promises serially)
PS: Yes, map / foreach is a parallel iteration, not a synchronous loop. Remember! Remember!