Node flow control

What is process control

In Node, flow control is the concept of sequential execution of a set of asynchronous tasks. Flow control can be either serial or parallel. Serial schematic diagram:

graph TD
Start --> work-1  --> work-2 --> ... --> work-n --> Stop

Parallel schematic diagram:

graph LR
Start --> work-1 --> Stop
Start --> work-2 --> Stop
Start --> ...    --> Stop
Start --> work-n --> Stop

Serial flow control

Serial application scenario

Serial flow control is a kind of control which is generated when related operations must be performed sequentially under certain circumstances. For example, if I want to create a new file test1.txt in a new directory test, and say hello world in test1.txt, and then create a new file test2.js to read hello word in test1.txt, there can be only one order. Even though file reads and writes are asynchronous, we have to wait for the previous step to complete before we can proceed to the next step.

Graph LR create directory test --> create new file test1.txt --> create helloword --> create new file test2.js --> write code to read test1.txt

Implement a serial method

  • Use timersetTimeoutTo simulate the

Here three timers are used to simulate. When the first timer callback is executed, the second timer will be suspended for the callback, and then the second callback will be executed. The third timer will be suspended for the callback, and finally the third callback will be executed.

graph LR
first --> next  --> last
setTimeout(() = > {
    console.log('I execute first.');
    setTimeout(() = > {
        console.log('I execute next.');
        setTimeout(() = > {
            console.log('I execute last.');
        }, 100);
    }, 500);
}, 1000);
Copy the code
  • useasyncLibrary implementation

Using the third-party open source library Async, sequential execution can be easily implemented by putting sequentially executed functions into arrays. Install Async before using it

npm i --save async
Copy the code
const async = require('async');
async.series([
  callback= > {
    setTimeout(() = > {
      console.log('I execute first.');
      callback();
    }, 1000);
  },
  callback= > {
    setTimeout(() = > {
      console.log('I execute next.');
      callback();
    }, 500);
  },
  callback= > {
    setTimeout(() = > {
      console.log('I execute last.');
      callback();
    }, 100); }]);Copy the code
  • Manual implementation

    Serialization process control essentially lets callbacks into play when needed, rather than simply nesting them. So let’s define one heretasks, use it to save each asynchronous function operation throughnext()And pass the result as an argument to the next function to ensure the order of execution.
const fs = require('fs');
const request = require('request');
const htmlparser = require('htmlparser');
const configFilename = './rss_feeds.txt';

function checkForRSSFile() {
  fs.exists(configFilename, (exists) = > {
    if(! exists)return next(new Error(`Missing RSS file: ${configFilename}`));
    next(null, configFilename);
  });
}

function readRSSFile(configFilename) {
  fs.readFile(configFilename, (err, feedList) = > {
    if (err) return next(err);
    feedList = feedList
      .toString()
      .replace(/^\s+|\s+$/g.' ')
      .split('\n');
    const random = Math.floor(Math.random() * feedList.length);
    next(null, feedList[random]);
  });
}

function downloadRSSFeed(feedUrl) {
  request({
    uri: feedUrl
  }, (err, res, body) = > {
    if (err) return next(err);
    if(res.statusCode ! = =200) return next(new Error('Abnormal response status code'));
    next(null, body);
  });
}

function parseRSSFeed(rss) {
  const handler = new htmlparser.RssHandler();
  const parser = new htmlparser.Parser(handler);
  parser.parseComplete(rss);
  if(! handler.dom.items.length)return next(new Error('No RSS items found'));
  const item = handler.dom.items.shift();
  console.log(item.title);
  console.log(item.link);
}
const tasks = [
  checkForRSSFile,
  readRSSFile,
  downloadRSSFeed,
  parseRSSFeed
];

function next(err, result) {
  if (err) throw err;
  const currentTask = tasks.shift();
  if (currentTask) {
    currentTask(result);
  }
}
next();
Copy the code

Parallel flow control

Parallel application scenarios

Request multiple resources and read multiple files simultaneously

A way to implement parallelism

To allow asynchronous tasks to run in parallel, you still put the tasks in an array, but the order in which the tasks are stored does not matter. Each task should call a handler function to increment the count of completed tasks. When all tasks are complete, the processor function should execute the subsequent logic. Use parallel flow control to count word frequency in several files:

Graph TD (1) graph TD (2) graph TD (2) graph TD --> Show word count gets a list of files in the directory --> Read file n --> show word count

Manual parallelism

const fs = require('fs');
const tasks = [];
const wordCounts = {};
const filesDir = './text';
let completedTasks = 0;

function checkIfComplete() {
  completedTasks++;
  if (completedTasks === tasks.length) {
    for (let index in wordCounts) {
      console.log(`${index}: ${wordCounts[index]}`); }}}function addWordCount(word) {
  wordCounts[word] = (wordCounts[word]) ? wordCounts[word] + 1 : 1;
}

function countWordsInText(text) {
  const words = text
    .toString()
    .toLowerCase()
    .split(/\W+/)
    .sort();
  words
    .filter(word= > word)
    .forEach(word= > addWordCount(word));
}

fs.readdir(filesDir, (err, files) = > {
  if (err) throw err;
  files.forEach(file= > {
    const task = (file= > {
      return () = > {
        fs.readFile(file, (err, text) = > {
          if (err) throwerr; countWordsInText(text); checkIfComplete(); }); }; }) (`${filesDir}/${file}`);
    tasks.push(task);
  })
  tasks.forEach(task= > task());
});
Copy the code