Node. Asynchrony of JS:
First, let's take a look at node JS asynchronous mechanism:
setTimeout(function () { console.log('event A occurs') }, 3000) console.log('event B occurs');
As a Java developer, the expected execution result of this code is:
- sleep 3 seconds
- event A occurs
- event B occurs
However, the actual implementation results are as follows:

ES6 provides promise, which is used to ensure that B event will not be triggered until A event is triggered
var q = new Promise(function (resolve, reject) { setTimeout(function () { console.log('event A occurs') resolve(); }, 3000) }) q.then(function () { console.log('event B occurs'); }, function (err) { console.log('err') })

The Promise object can be compared to a container. There is an asynchronous operation in the container. The container will call the then method only when it receives a semaphore (resolve or reject).
Consider the following code:
var q = new Promise(function (resolve, reject) { setTimeout(function () { console.log('event A occurs') resolve(); }, 3000) }) q.then(function () { console.log('event B occurs'); }, function (err) { console.log('err') }) console.log("event C occurs")
Operation results:

In addition to Promise, node can also be implemented through Async library JS method is executed synchronously
Async for synchronization
The syntax of Async is a bit similar to the thread pool in Java. Submit tasks, and the thread pool controls the execution of tasks
ExecutorService service = Executors.newFixedThreadPool(5); service.submit(new Runnable() { @Override public void run() { } });
async supports various models such as series / parallel / parallel limit / waterfall
- series: serial You can accept an array as a parameter and the method that needs serial processing as the element of the array
var async = require('async'); async.series( [ function (callback) { setTimeout(function () { console.log('event A occurs') callback(null, 'A') // The first parameter is the exception error, and the second parameter is the return value }, 3000) }, function (callback) { console.log('event B occurs'); callback(null, 'B') } ], function (err, results) { // results is an array of return values console.log('event ' + results[0] + results[1] + ' occurs') } ) console.log("event D occurs")

async.series can also accept objects as parameters. The code is as follows:
async.series( { A: function (callback) { setTimeout(function () { console.log('event A occurs') callback(null, 'A') // The first parameter is the exception error, and the second parameter is the return value }, 3000) }, B: function (callback) { console.log('event B occurs'); callback(null, 'B') } }, function (err, results) { // Results is the collection of all return values, results A gets the return value of A. console.log('event ' + results.A + results.B + ' occurs') } ) console.log("event D occurs")

Let's take a look at the comments of the series:
/** * Run the functions in the `tasks` collection in series, each one running once * the previous function has completed. If any functions in the series pass an * error to its callback, no more functions are run, and `callback` is * immediately called with the value of the error. Otherwise, `callback` * receives an array of results when `tasks` have completed. * * It is also possible to use an object instead of an array. Each property will * be run as a function, and the results will be passed to the final `callback` * as an object instead of an array. This can be a more readable way of handling * results from {@link async.series}. * * **Note** that while many implementations preserve the order of object * properties, the [ECMAScript Language Specification](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6) * explicitly states that * * > The mechanics and order of enumerating the properties is not specified. * * So if you rely on the order in which your series of functions are executed, * and want this to work on all platforms, consider using an array. * * @name series * @static * @memberOf module:ControlFlow * @method * @category Control Flow * @param {Array|Iterable|Object} tasks - A collection containing * [async functions]{@link AsyncFunction} to run in series. * Each function can complete with any number of optional `result` values. * @param {Function} [callback] - An optional callback to run once all the * functions have completed. This function gets a results array (or object) * containing all the result arguments passed to the `task` callbacks. Invoked * with (err, result). * @example * async.series([ * function(callback) { * // do some stuff ... * callback(null, 'one'); * }, * function(callback) { * // do some more stuff ... * callback(null, 'two'); * } * ], * // optional callback * function(err, results) { * // results is now equal to ['one', 'two'] * }); * * async.series({ * one: function(callback) { * setTimeout(function() { * callback(null, 1); * }, 200); * }, * two: function(callback){ * setTimeout(function() { * callback(null, 2); * }, 100); * } * }, function(err, results) { * // results is now equal to: {one: 1, two: 2} * }); */ function series(tasks, callback) { _parallel(eachOfSeries, tasks, callback); }
The key points of translation are as follows: 1. Serial execution 2. If any method returns an abnormal error in the callback, stop the execution of subsequent methods, and async The callback of series is executed immediately.
- Parallel: parallel
async.parallel( [ function (callback) { setTimeout(function () { console.log('event A occurs') callback(null, 'A') }, 3000) }, function (callback) { console.log('event B occurs') callback(null, 'B') } ], function (err, result) { console.log("event C occurs") } ) console.log("event D occurs")

async.parallel comments:
/** * Run the `tasks` collection of functions in parallel, without waiting until * the previous function has completed. If any of the functions pass an error to * its callback, the main `callback` is immediately called with the value of the * error. Once the `tasks` have completed, the results are passed to the final * `callback` as an array. * * **Note:** `parallel` is about kicking-off I/O tasks in parallel, not about * parallel execution of code. If your tasks do not use any timers or perform * any I/O, they will actually be executed in series. Any synchronous setup * sections for each task will happen one after the other. JavaScript remains * single-threaded. * * **Hint:** Use [`reflect`]{@link module:Utils.reflect} to continue the * execution of other tasks when a task fails. * * It is also possible to use an object instead of an array. Each property will * be run as a function and the results will be passed to the final `callback` * as an object instead of an array. This can be a more readable way of handling * results from {@link async.parallel}. * * @name parallel * @static * @memberOf module:ControlFlow * @method * @category Control Flow * @param {Array|Iterable|Object} tasks - A collection of * [async functions]{@link AsyncFunction} to run. * Each async function can complete with any number of optional `result` values. * @param {Function} [callback] - An optional callback to run once all the * functions have completed successfully. This function gets a results array * (or object) containing all the result arguments passed to the task callbacks. * Invoked with (err, results). * * @example * async.parallel([ * function(callback) { * setTimeout(function() { * callback(null, 'one'); * }, 200); * }, * function(callback) { * setTimeout(function() { * callback(null, 'two'); * }, 100); * } * ], * // optional callback * function(err, results) { * // the results array will equal ['one','two'] even though * // the second function had a shorter timeout. * }); * * // an example using an object instead of an array * async.parallel({ * one: function(callback) { * setTimeout(function() { * callback(null, 1); * }, 200); * }, * two: function(callback) { * setTimeout(function() { * callback(null, 2); * }, 100); * } * }, function(err, results) { * // results is now equals to: {one: 1, two: 2} * }); */ function parallelLimit(tasks, callback) { _parallel(eachOf, tasks, callback); }
main points: 1. Parallel execution. After all tasks are executed, the callback function is executed immediately. 2. If a task execution exception reports an error, execute the callback function immediately.
- Parallel limit: each method is executed in parallel, but there is an upper limit on the number of parallel tasks at the same time
async.parallelLimit( [ function (callback) { setTimeout(function () { console.log('event A occurs') callback(null, 'A') }, 3000) }, function (callback) { console.log('event B occurs') callback(null, 'B') } ], 1, function (err, result) { console.log("event C occurs") } ) console.log("event D occurs")

Comments on parallelLimit:
/** * The same as [`parallel`]{@link module:ControlFlow.parallel} but runs a maximum of `limit` async operations at a * time. * * @name parallelLimit * @static * @memberOf module:ControlFlow * @method * @see [async.parallel]{@link module:ControlFlow.parallel} * @category Control Flow * @param {Array|Iterable|Object} tasks - A collection of * [async functions]{@link AsyncFunction} to run. * Each async function can complete with any number of optional `result` values. * @param {number} limit - The maximum number of async operations at a time. * @param {Function} [callback] - An optional callback to run once all the * functions have completed successfully. This function gets a results array * (or object) containing all the result arguments passed to the task callbacks. * Invoked with (err, results). */ function parallelLimit$1(tasks, limit, callback) { _parallel(_eachOfLimit(limit), tasks, callback); }
main points: 1. The processing logic is the same as the parallel method, except that there is an upper limit on the number of tasks executed in parallel at the same time.
- waterfall: serial, the result of the previous task is passed to the next task.
async.waterfall( [ function (callback) { setTimeout(function () { console.log('event A occurs') callback(null, 'B') // The first parameter is error, and the second parameter is the parameter of the next task }, 3000) }, function (arg1, callback) { // arg1 equals B console.log('event ' + arg1 + ' occurs') callback(null, 'C') } ], function (err, result) { // result equals C console.log('event ' + result + ' occurs') } ) console.log("event D occurs")

Which interfaces does async expose
In addition to the above commonly used interfaces, async starts from / async / dist / async JS can see all interfaces exposed by async:
exports['default'] = index; exports.applyEach = applyEach; exports.applyEachSeries = applyEachSeries; exports.apply = apply; exports.asyncify = asyncify; exports.auto = auto; exports.autoInject = autoInject; exports.cargo = cargo; exports.compose = compose; exports.concat = concat; exports.concatLimit = concatLimit; exports.concatSeries = concatSeries; exports.constant = constant; exports.detect = detect; exports.detectLimit = detectLimit; exports.detectSeries = detectSeries; exports.dir = dir; exports.doDuring = doDuring; exports.doUntil = doUntil; exports.doWhilst = doWhilst; exports.during = during; exports.each = eachLimit; exports.eachLimit = eachLimit$1; exports.eachOf = eachOf; exports.eachOfLimit = eachOfLimit; exports.eachOfSeries = eachOfSeries; exports.eachSeries = eachSeries; exports.ensureAsync = ensureAsync; exports.every = every; exports.everyLimit = everyLimit; exports.everySeries = everySeries; exports.filter = filter; exports.filterLimit = filterLimit; exports.filterSeries = filterSeries; exports.forever = forever; exports.groupBy = groupBy; exports.groupByLimit = groupByLimit; exports.groupBySeries = groupBySeries; exports.log = log; exports.map = map; exports.mapLimit = mapLimit; exports.mapSeries = mapSeries; exports.mapValues = mapValues; exports.mapValuesLimit = mapValuesLimit; exports.mapValuesSeries = mapValuesSeries; exports.memoize = memoize; exports.nextTick = nextTick; exports.parallel = parallelLimit; exports.parallelLimit = parallelLimit$1; exports.priorityQueue = priorityQueue; exports.queue = queue$1; exports.race = race; exports.reduce = reduce; exports.reduceRight = reduceRight; exports.reflect = reflect; exports.reflectAll = reflectAll; exports.reject = reject; exports.rejectLimit = rejectLimit; exports.rejectSeries = rejectSeries; exports.retry = retry; exports.retryable = retryable; exports.seq = seq; exports.series = series; exports.setImmediate = setImmediate$1; exports.some = some; exports.someLimit = someLimit; exports.someSeries = someSeries; exports.sortBy = sortBy; exports.timeout = timeout; exports.times = times; exports.timesLimit = timeLimit; exports.timesSeries = timesSeries; exports.transform = transform; exports.tryEach = tryEach; exports.unmemoize = unmemoize; exports.until = until; exports.waterfall = waterfall; exports.whilst = whilst; exports.all = every; exports.allLimit = everyLimit; exports.allSeries = everySeries; exports.any = some; exports.anyLimit = someLimit; exports.anySeries = someSeries; exports.find = detect; exports.findLimit = detectLimit; exports.findSeries = detectSeries; exports.forEach = eachLimit; exports.forEachSeries = eachSeries; exports.forEachLimit = eachLimit$1; exports.forEachOf = eachOf; exports.forEachOfSeries = eachOfSeries; exports.forEachOfLimit = eachOfLimit; exports.inject = reduce; exports.foldl = reduce; exports.foldr = reduceRight; exports.select = filter; exports.selectLimit = filterLimit; exports.selectSeries = filterSeries; exports.wrapSync = asyncify;