This is the 77th article written without water. If you want to get more original articles, please search the public account to follow us. This article was first published on the cloud blog: Writing high-quality maintainable code — Asynchronous optimization

preface

In the current front-end development, the frequency of asynchronous operation has become more and more high, especially for the use of data interface request and timer, so we have to pay attention to the asynchronous in the business encounter scenarios, as well as the optimization of asynchronous. Incorrect asynchronous processing can cause many problems, such as page rendering, reloading, and so on.

Below, we will simply start from JavaScript which asynchronous types there are roughly as a starting point, and then list some scenarios we will encounter in the business to analyze one by one, how we should solve the problem.

Asynchronous implementation types

First of all, there are roughly the following types of asynchronous implementation:

callback

Callback is a callback function. This guy came along very, very early, and he’s actually the basic way of dealing with asynchrony. And the concept of callbacks is not unique to JavaScript; you’ll find it in backend languages like Java or C#.

A callback function is simply a function that passes arguments to another host function. The process of invoking and executing a callback function after the host function completes execution or reaches a specific stage, and then returning the result of execution to the host function.

For example, the familiar setTimeout or the second method of setState in React uses callback functions to solve asynchronous implementation.

setTimeout(() = > {
   // Wait 0.2s before performing specific operations
   this.doSomething();
}, 200);
this.setState({
  count: res.count,
}, () = > {
  // Do the specific business operation after the count is updated
  this.doSomething();
});
Copy the code

Promise

Promise is a nice thing, because with it we can do a lot of things with asynchrony, and we can do asynchrony in a chained fashion.

As a matter of fact, deferred in JQuery is similar to it. Both of them use a callback solution and can make chain calls. However, the error catch method is added to Promise to handle exception scenarios more conveniently, and it has built-in states (resolve, reject, reject, etc.). Pending), the state can only change from pending to one of the other two, and the change cannot be reversed or modified again.

let promise = new Promise((resolve, reject) = > { 
  reject("I'm sorry, you're not my type.");
});
promise.then((data) = > {
console.log('First success' + data);
  return 'First success' + data
},(error) = > {
console.log(error) }
).then((data2) = > {
  console.log('Second Success' + data2);
},(error2) = > { 
  console.log(error2) }
).catch((e) = > {
  console.log('Caught my mistake' + e);
});
Copy the code

await/async

Await /async is actually an upgraded version of Promise. When we call asynchro with await/async, we shall execute asynchro from top to bottom in order, just like writing synchronous code. This is more in line with our code writing habits and thinking logic, so it is easy to understand. The overall code logic will also be clearer.

async function asyncDemoFn() {
  const data1 = await getData1();
  const data2 = await getData2(data1);
  const data3 =  await getData3(data2);
  console.log(data3)
}
await asyncDemoFn()
Copy the code

generator

Generator is a new thing in ES6, and I’m sure many people don’t have access to it in real code, so it’s relatively obscure, but it’s pretty powerful. In short, it controls asynchronous calls and is actually a state machine.

function* foo() {
  for (let i = 1; i <= 3; i++) {
    let x = yield 'Just give me a second, I =${i}`;
    console.log(x); }}setTimeout(() = > {
  console.log('It's finally my turn.');
}, 1);
var a = foo();
console.log(a); // foo {<closed>}
var b = a.next();
console.log(b); // {value: "I = 1", done: false}
var c = a.next();
console.log(c); // {value: "I = 2", done: false}
var d = a.next();
console.log(d); // {value: "I = 3", done: false}
var e = a.next();
console.log(e); // {value: undefined, done: true}
// It's my turn at last
Copy the code

The function foo in the above code is a coroutine, and its power is the yield command. It says execution at this point, execution will be handed over to some other coroutine. That is, the yield command is the boundary between the two phases of asynchrony.

The coroutine pauses at yield, returns to execution, and continues from where it was suspended. The biggest advantage of this is that the code is written very much like a synchronous operation, which is exactly the same without the yield command.

Let’s use generator in a more context-friendly way. For example, if we need to perform checkAuth and checkAddress checks automatically in the page, we will use the generator to automatically check the above two asynchronous checks.

const checkAuth = () = > {
    return new Promise((resolve) = >{
        setTimeout(() = >{
           resolve('checkAuth1')},1000)})}const checkAddress = () = > {
    return new Promise((resolve) = >{
        setTimeout(() = >{
            resolve('checkAddress2')},2000)})}var steps = [checkAuth,checkAddress]
function* foo(checkList) {
  for (let i = 0; i < checkList.length; i++) {
    let x = yield checkList[i]();
    console.log(x); }}var stepsGen = foo(steps)
var run = async (gen)=>{
    var isFinnish = false
    do{
       const {done,value} = gen.next()
       console.log('done:',done)
       console.log('value:',value)
       const result = await value
       console.log('result:',result)
       
       isFinnish = done
    }while(! isFinnish)console.log('isFinnish:',isFinnish)
}
run(stepsGen)
Copy the code

Kinds of contrast

  • From morning to night in the temporal dimension: callback, Promise, generator, await/async
  • Await /async is currently the ultimate form of asynchrony
  • Callback gives us a basic way to handle asynchronous situations. Promise says goodbye to callback’s callback hell and adds resolve, reject, and catch to allow us to handle different situations. Generator increases asynchrony. Similar to a state machine that can temporarily suspend multiple asynchronous executions and then continue to execute the remaining asynchronous calls at an appropriate time, await/async makes asynchronous calls more semantic and performs asynchrony automatically

Scenarios encountered in asynchronous services

The callback hell

When using A callback function, we may have A situation where B needs to continue calling after A returns, so there is A problem called callback hell in this sequence.

getData1().then((resData1) = > {
  getData2(resData1).then((resData2) = > {
  getData3(resData2).then((resData3) = >{
    console.log('resData3:', resData3)
    })
  });
});
Copy the code

In this case we can try to solve the problem with multiple deep nesting with await/async mode.

async function asyncDemoFn2() {
  const resData1 = await getData1();
  const resData2 = await getData2(resData1);
  const resData3 =  await getData3(resData2);
  console.log(resData3)
}
await asyncDemoFn2()
Copy the code

Asynchronous loop

In business, the most common problem we encounter is the order of multiple asynchronous calls, which can be roughly divided into the following:

Parallel execution

When executing in parallel, we can simply use Promise’s all method

Promise.all([getData1(),getData2(),getData3()]).then(res={
	console.log('res:',res)
})
Copy the code

sequential

In sequential execution, we can do this in two ways

  1. Use async/await with for
const sources = [getData1,getData2,getData3]
async function promiseQueue() {
  console.log('开始');
  for (let targetSource in sources) {
    await targetSource();
  }
  console.log('complete');
};
promiseQueue()
Copy the code
  1. Use async/await with while
/ / getData1 getData2, getData3 as promise object
const sources = [getData1,getData2,getData3]
async function promiseQueue() {
  let index = 0
  console.log('开始');
  while(index >=0 && index < sources.length){
    await targetSource();
    index++
  }
  console.log('complete');
};
promiseQueue()
Copy the code
  1. Use async/await with reduce
/ / getData1 getData2, getData3 as promise object
const sources = [getData1,getData2,getData3]
sources.reduce(async (previousValue, currentValue)=>{
  await previousValue
  return currentValue()
},Promise.resolve())
Copy the code
  1. Using a recursive
const sources = [getData1,getData2,getData3]
function promiseQueue(list , index = 0) {
  const len = list.length
  console.log('开始');
  if(index >= 0 && index < len){
    list[index]().then(() = >{
      promiseQueue(list, index+1)})}console.log('complete');
}
promiseQueue(sources)
Copy the code

At the end

Today is just a discussion of common usage scenarios for asynchrony, with some simple examples. There are many, many complex usage scenarios for asynchrony. More ideas are waiting for you.

reference

JS asynchronous programming six schemes

6 reasons Async/Await should replace Promise

Four approaches to Asynchronous programming in Javascript

Recommended reading

How to build a performance detection system from 0 to 1

Talk about my understanding of FaaS in combination with Ali Cloud FC

, recruiting

ZooTeam, a young passionate and creative front-end team, belongs to the PRODUCT R&D department of ZooTeam, based in picturesque Hangzhou. The team now has more than 40 front-end partners, with an average age of 27, and nearly 30% of them are full-stack engineers, no problem in the youth storm group. The members consist of “old” soldiers from Alibaba and netease, as well as fresh graduates from Zhejiang University, University of Science and Technology of China, Hangzhou Electric And other universities. In addition to daily business docking, the team also carried out technical exploration and practice in material system, engineering platform, building platform, performance experience, cloud application, data analysis and visualization, promoted and implemented a series of internal technical products, and continued to explore the new boundary of front-end technology system.

If you want to change what’s been bothering you, you want to start bothering you. If you want to change, you’ve been told you need more ideas, but you don’t have a solution. If you want change, you have the power to make it happen, but you don’t need it. If you want to change what you want to accomplish, you need a team to support you, but you don’t have the position to lead people. If you want to change the pace, it will be “5 years and 3 years of experience”; If you want to change the original savvy is good, but there is always a layer of fuzzy window… If you believe in the power of believing, believing that ordinary people can achieve extraordinary things, believing that you can meet a better version of yourself. If you want to be a part of the process of growing a front end team with deep business understanding, sound technology systems, technology value creation, and impact spillover as your business takes off, I think we should talk. Any time, waiting for you to write something and send it to [email protected]