Detailed Explanation of Serial Execution of Javascript Asynchronous Flow Control

  • 2021-08-28 18:52:37
  • OfStack

This article mainly talks about how to serially execute a group of asynchronous tasks, such as the following tasks. Here we use setTimeout to simulate a asynchronous task:


let taskA = () => setTimeout(() => console.log('run task A'), 100);
let taskB = () => setTimeout(() => console.log('run task B'), 50);
let taskC = () => setTimeout(() => console.log('run task C'), 150);

Direct operation


taskA(); taskB(); taskC();

It can't achieve the effect of executing A, B and C in sequence.

First of all, let's look at the most traditional practice of 1, calling the next 1 task after the execution of 1 task is completed through callback:


let taskA = setTimeout(() => {
 console.log('run task A');
 taskB();
}, 100);
let taskB = setTimeout(() => {
 console.log('run task B');
 taskC();
}, 50);
let taskC = setTimeout(() => {
 console.log('run task B');
}, 150);

The second method is to encapsulate every task as a function that returns Promise, and then use a chain call using Promise for serial execution:


let taskA = () => new Promise((resolve, reject) => {
 setTimeout(() => {
  console.log('run task A');
  resolve();
 }, 100);
})
let taskB = () => new Promise((resolve, reject) => {
 setTimeout(() => {
  console.log('run task B');
  resolve();
 }, 50);
})
let taskC = () => new Promise((resolve, reject) => {
 setTimeout(() => {
  console.log('run task C');
  resolve();
 }, 150);
})
function runTasks2() {
  console.log('tasks 2');
  taskA().then(taskB).then(taskC);
}

Assuming that the number of tasks is uncertain, they can be executed in the following ways:


function runTasks3(tasks) {
  console.log('tasks 3');
  let pro = tasks[0]();
  for (let i = 1; i < tasks.length; i++) {
  pro.then(tasks[i]);
  }
}

With the help of async and await of es7, we can also write the above function in one way:


async function runTasks3_1(tasks) {
  for (let i = 0; i < tasks.length; i++) {
  await tasks[i]();
  }
}

At the end of the article, we implement a serial executor for performing a set of serial tasks:


function async(tasks) {
  const count = tasks.length;
  let index = 0;
  const next = () => {
   if (index >= count) return;
   const task = tasks[index++];
   task(next);
  }
  next(0);
}

The function is used as follows:


 
 async([
   next => setTimeout(() => { console.log('taskA ...'); next() }, 100),
   next => setTimeout(() => { console.log('taskB ...'); next() }, 50),
   next => setTimeout(() => { console.log('taskC ...'); next() }, 30)
 ]);
 

In every 1 subtask, we continue to execute the next 1 subtask by calling next function.

In specific use, you may encounter the situation of passing parameters between functions, that is, the execution result of the first task needs to be used as the input parameter of the next task, which can be slightly modified from the above example ~ ~


Related articles: