'use strict';12Object.defineProperty(exports, "__esModule", {3value: true4});5exports.default = auto;67var _once = require('./internal/once.js');89var _once2 = _interopRequireDefault(_once);1011var _onlyOnce = require('./internal/onlyOnce.js');1213var _onlyOnce2 = _interopRequireDefault(_onlyOnce);1415var _wrapAsync = require('./internal/wrapAsync.js');1617var _wrapAsync2 = _interopRequireDefault(_wrapAsync);1819var _promiseCallback = require('./internal/promiseCallback.js');2021function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }2223/**24* Determines the best order for running the {@link AsyncFunction}s in `tasks`, based on25* their requirements. Each function can optionally depend on other functions26* being completed first, and each function is run as soon as its requirements27* are satisfied.28*29* If any of the {@link AsyncFunction}s pass an error to their callback, the `auto` sequence30* will stop. Further tasks will not execute (so any other functions depending31* on it will not run), and the main `callback` is immediately called with the32* error.33*34* {@link AsyncFunction}s also receive an object containing the results of functions which35* have completed so far as the first argument, if they have dependencies. If a36* task function has no dependencies, it will only be passed a callback.37*38* @name auto39* @static40* @memberOf module:ControlFlow41* @method42* @category Control Flow43* @param {Object} tasks - An object. Each of its properties is either a44* function or an array of requirements, with the {@link AsyncFunction} itself the last item45* in the array. The object's key of a property serves as the name of the task46* defined by that property, i.e. can be used when specifying requirements for47* other tasks. The function receives one or two arguments:48* * a `results` object, containing the results of the previously executed49* functions, only passed if the task has any dependencies,50* * a `callback(err, result)` function, which must be called when finished,51* passing an `error` (which can be `null`) and the result of the function's52* execution.53* @param {number} [concurrency=Infinity] - An optional `integer` for54* determining the maximum number of tasks that can be run in parallel. By55* default, as many as possible.56* @param {Function} [callback] - An optional callback which is called when all57* the tasks have been completed. It receives the `err` argument if any `tasks`58* pass an error to their callback. Results are always returned; however, if an59* error occurs, no further `tasks` will be performed, and the results object60* will only contain partial results. Invoked with (err, results).61* @returns {Promise} a promise, if a callback is not passed62* @example63*64* //Using Callbacks65* async.auto({66* get_data: function(callback) {67* // async code to get some data68* callback(null, 'data', 'converted to array');69* },70* make_folder: function(callback) {71* // async code to create a directory to store a file in72* // this is run at the same time as getting the data73* callback(null, 'folder');74* },75* write_file: ['get_data', 'make_folder', function(results, callback) {76* // once there is some data and the directory exists,77* // write the data to a file in the directory78* callback(null, 'filename');79* }],80* email_link: ['write_file', function(results, callback) {81* // once the file is written let's email a link to it...82* callback(null, {'file':results.write_file, 'email':'[email protected]'});83* }]84* }, function(err, results) {85* if (err) {86* console.log('err = ', err);87* }88* console.log('results = ', results);89* // results = {90* // get_data: ['data', 'converted to array']91* // make_folder; 'folder',92* // write_file: 'filename'93* // email_link: { file: 'filename', email: '[email protected]' }94* // }95* });96*97* //Using Promises98* async.auto({99* get_data: function(callback) {100* console.log('in get_data');101* // async code to get some data102* callback(null, 'data', 'converted to array');103* },104* make_folder: function(callback) {105* console.log('in make_folder');106* // async code to create a directory to store a file in107* // this is run at the same time as getting the data108* callback(null, 'folder');109* },110* write_file: ['get_data', 'make_folder', function(results, callback) {111* // once there is some data and the directory exists,112* // write the data to a file in the directory113* callback(null, 'filename');114* }],115* email_link: ['write_file', function(results, callback) {116* // once the file is written let's email a link to it...117* callback(null, {'file':results.write_file, 'email':'[email protected]'});118* }]119* }).then(results => {120* console.log('results = ', results);121* // results = {122* // get_data: ['data', 'converted to array']123* // make_folder; 'folder',124* // write_file: 'filename'125* // email_link: { file: 'filename', email: '[email protected]' }126* // }127* }).catch(err => {128* console.log('err = ', err);129* });130*131* //Using async/await132* async () => {133* try {134* let results = await async.auto({135* get_data: function(callback) {136* // async code to get some data137* callback(null, 'data', 'converted to array');138* },139* make_folder: function(callback) {140* // async code to create a directory to store a file in141* // this is run at the same time as getting the data142* callback(null, 'folder');143* },144* write_file: ['get_data', 'make_folder', function(results, callback) {145* // once there is some data and the directory exists,146* // write the data to a file in the directory147* callback(null, 'filename');148* }],149* email_link: ['write_file', function(results, callback) {150* // once the file is written let's email a link to it...151* callback(null, {'file':results.write_file, 'email':'[email protected]'});152* }]153* });154* console.log('results = ', results);155* // results = {156* // get_data: ['data', 'converted to array']157* // make_folder; 'folder',158* // write_file: 'filename'159* // email_link: { file: 'filename', email: '[email protected]' }160* // }161* }162* catch (err) {163* console.log(err);164* }165* }166*167*/168function auto(tasks, concurrency, callback) {169if (typeof concurrency !== 'number') {170// concurrency is optional, shift the args.171callback = concurrency;172concurrency = null;173}174callback = (0, _once2.default)(callback || (0, _promiseCallback.promiseCallback)());175var numTasks = Object.keys(tasks).length;176if (!numTasks) {177return callback(null);178}179if (!concurrency) {180concurrency = numTasks;181}182183var results = {};184var runningTasks = 0;185var canceled = false;186var hasError = false;187188var listeners = Object.create(null);189190var readyTasks = [];191192// for cycle detection:193var readyToCheck = []; // tasks that have been identified as reachable194// without the possibility of returning to an ancestor task195var uncheckedDependencies = {};196197Object.keys(tasks).forEach(key => {198var task = tasks[key];199if (!Array.isArray(task)) {200// no dependencies201enqueueTask(key, [task]);202readyToCheck.push(key);203return;204}205206var dependencies = task.slice(0, task.length - 1);207var remainingDependencies = dependencies.length;208if (remainingDependencies === 0) {209enqueueTask(key, task);210readyToCheck.push(key);211return;212}213uncheckedDependencies[key] = remainingDependencies;214215dependencies.forEach(dependencyName => {216if (!tasks[dependencyName]) {217throw new Error('async.auto task `' + key + '` has a non-existent dependency `' + dependencyName + '` in ' + dependencies.join(', '));218}219addListener(dependencyName, () => {220remainingDependencies--;221if (remainingDependencies === 0) {222enqueueTask(key, task);223}224});225});226});227228checkForDeadlocks();229processQueue();230231function enqueueTask(key, task) {232readyTasks.push(() => runTask(key, task));233}234235function processQueue() {236if (canceled) return;237if (readyTasks.length === 0 && runningTasks === 0) {238return callback(null, results);239}240while (readyTasks.length && runningTasks < concurrency) {241var run = readyTasks.shift();242run();243}244}245246function addListener(taskName, fn) {247var taskListeners = listeners[taskName];248if (!taskListeners) {249taskListeners = listeners[taskName] = [];250}251252taskListeners.push(fn);253}254255function taskComplete(taskName) {256var taskListeners = listeners[taskName] || [];257taskListeners.forEach(fn => fn());258processQueue();259}260261function runTask(key, task) {262if (hasError) return;263264var taskCallback = (0, _onlyOnce2.default)((err, ...result) => {265runningTasks--;266if (err === false) {267canceled = true;268return;269}270if (result.length < 2) {271[result] = result;272}273if (err) {274var safeResults = {};275Object.keys(results).forEach(rkey => {276safeResults[rkey] = results[rkey];277});278safeResults[key] = result;279hasError = true;280listeners = Object.create(null);281if (canceled) return;282callback(err, safeResults);283} else {284results[key] = result;285taskComplete(key);286}287});288289runningTasks++;290var taskFn = (0, _wrapAsync2.default)(task[task.length - 1]);291if (task.length > 1) {292taskFn(results, taskCallback);293} else {294taskFn(taskCallback);295}296}297298function checkForDeadlocks() {299// Kahn's algorithm300// https://en.wikipedia.org/wiki/Topological_sorting#Kahn.27s_algorithm301// http://connalle.blogspot.com/2013/10/topological-sortingkahn-algorithm.html302var currentTask;303var counter = 0;304while (readyToCheck.length) {305currentTask = readyToCheck.pop();306counter++;307getDependents(currentTask).forEach(dependent => {308if (--uncheckedDependencies[dependent] === 0) {309readyToCheck.push(dependent);310}311});312}313314if (counter !== numTasks) {315throw new Error('async.auto cannot execute tasks due to a recursive dependency');316}317}318319function getDependents(taskName) {320var result = [];321Object.keys(tasks).forEach(key => {322const task = tasks[key];323if (Array.isArray(task) && task.indexOf(taskName) >= 0) {324result.push(key);325}326});327return result;328}329330return callback[_promiseCallback.PROMISE_SYMBOL];331}332module.exports = exports['default'];333334