node.js realizes method synchronization through Async

Posted by jovanross on Wed, 05 Jan 2022 19:26:34 +0100

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;

Topics: node.js