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 timer
setTimeout
To 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
- use
async
Library 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