Spaces:
Sleeping
Sleeping
| ; | |
| Object.defineProperty(exports, "__esModule", { | |
| value: true | |
| }); | |
| exports.default = auto; | |
| var _once = require('./internal/once.js'); | |
| var _once2 = _interopRequireDefault(_once); | |
| var _onlyOnce = require('./internal/onlyOnce.js'); | |
| var _onlyOnce2 = _interopRequireDefault(_onlyOnce); | |
| var _wrapAsync = require('./internal/wrapAsync.js'); | |
| var _wrapAsync2 = _interopRequireDefault(_wrapAsync); | |
| var _promiseCallback = require('./internal/promiseCallback.js'); | |
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | |
| /** | |
| * Determines the best order for running the {@link AsyncFunction}s in `tasks`, based on | |
| * their requirements. Each function can optionally depend on other functions | |
| * being completed first, and each function is run as soon as its requirements | |
| * are satisfied. | |
| * | |
| * If any of the {@link AsyncFunction}s pass an error to their callback, the `auto` sequence | |
| * will stop. Further tasks will not execute (so any other functions depending | |
| * on it will not run), and the main `callback` is immediately called with the | |
| * error. | |
| * | |
| * {@link AsyncFunction}s also receive an object containing the results of functions which | |
| * have completed so far as the first argument, if they have dependencies. If a | |
| * task function has no dependencies, it will only be passed a callback. | |
| * | |
| * @name auto | |
| * @static | |
| * @memberOf module:ControlFlow | |
| * @method | |
| * @category Control Flow | |
| * @param {Object} tasks - An object. Each of its properties is either a | |
| * function or an array of requirements, with the {@link AsyncFunction} itself the last item | |
| * in the array. The object's key of a property serves as the name of the task | |
| * defined by that property, i.e. can be used when specifying requirements for | |
| * other tasks. The function receives one or two arguments: | |
| * * a `results` object, containing the results of the previously executed | |
| * functions, only passed if the task has any dependencies, | |
| * * a `callback(err, result)` function, which must be called when finished, | |
| * passing an `error` (which can be `null`) and the result of the function's | |
| * execution. | |
| * @param {number} [concurrency=Infinity] - An optional `integer` for | |
| * determining the maximum number of tasks that can be run in parallel. By | |
| * default, as many as possible. | |
| * @param {Function} [callback] - An optional callback which is called when all | |
| * the tasks have been completed. It receives the `err` argument if any `tasks` | |
| * pass an error to their callback. Results are always returned; however, if an | |
| * error occurs, no further `tasks` will be performed, and the results object | |
| * will only contain partial results. Invoked with (err, results). | |
| * @returns {Promise} a promise, if a callback is not passed | |
| * @example | |
| * | |
| * //Using Callbacks | |
| * async.auto({ | |
| * get_data: function(callback) { | |
| * // async code to get some data | |
| * callback(null, 'data', 'converted to array'); | |
| * }, | |
| * make_folder: function(callback) { | |
| * // async code to create a directory to store a file in | |
| * // this is run at the same time as getting the data | |
| * callback(null, 'folder'); | |
| * }, | |
| * write_file: ['get_data', 'make_folder', function(results, callback) { | |
| * // once there is some data and the directory exists, | |
| * // write the data to a file in the directory | |
| * callback(null, 'filename'); | |
| * }], | |
| * email_link: ['write_file', function(results, callback) { | |
| * // once the file is written let's email a link to it... | |
| * callback(null, {'file':results.write_file, 'email':'user@example.com'}); | |
| * }] | |
| * }, function(err, results) { | |
| * if (err) { | |
| * console.log('err = ', err); | |
| * } | |
| * console.log('results = ', results); | |
| * // results = { | |
| * // get_data: ['data', 'converted to array'] | |
| * // make_folder; 'folder', | |
| * // write_file: 'filename' | |
| * // email_link: { file: 'filename', email: 'user@example.com' } | |
| * // } | |
| * }); | |
| * | |
| * //Using Promises | |
| * async.auto({ | |
| * get_data: function(callback) { | |
| * console.log('in get_data'); | |
| * // async code to get some data | |
| * callback(null, 'data', 'converted to array'); | |
| * }, | |
| * make_folder: function(callback) { | |
| * console.log('in make_folder'); | |
| * // async code to create a directory to store a file in | |
| * // this is run at the same time as getting the data | |
| * callback(null, 'folder'); | |
| * }, | |
| * write_file: ['get_data', 'make_folder', function(results, callback) { | |
| * // once there is some data and the directory exists, | |
| * // write the data to a file in the directory | |
| * callback(null, 'filename'); | |
| * }], | |
| * email_link: ['write_file', function(results, callback) { | |
| * // once the file is written let's email a link to it... | |
| * callback(null, {'file':results.write_file, 'email':'user@example.com'}); | |
| * }] | |
| * }).then(results => { | |
| * console.log('results = ', results); | |
| * // results = { | |
| * // get_data: ['data', 'converted to array'] | |
| * // make_folder; 'folder', | |
| * // write_file: 'filename' | |
| * // email_link: { file: 'filename', email: 'user@example.com' } | |
| * // } | |
| * }).catch(err => { | |
| * console.log('err = ', err); | |
| * }); | |
| * | |
| * //Using async/await | |
| * async () => { | |
| * try { | |
| * let results = await async.auto({ | |
| * get_data: function(callback) { | |
| * // async code to get some data | |
| * callback(null, 'data', 'converted to array'); | |
| * }, | |
| * make_folder: function(callback) { | |
| * // async code to create a directory to store a file in | |
| * // this is run at the same time as getting the data | |
| * callback(null, 'folder'); | |
| * }, | |
| * write_file: ['get_data', 'make_folder', function(results, callback) { | |
| * // once there is some data and the directory exists, | |
| * // write the data to a file in the directory | |
| * callback(null, 'filename'); | |
| * }], | |
| * email_link: ['write_file', function(results, callback) { | |
| * // once the file is written let's email a link to it... | |
| * callback(null, {'file':results.write_file, 'email':'user@example.com'}); | |
| * }] | |
| * }); | |
| * console.log('results = ', results); | |
| * // results = { | |
| * // get_data: ['data', 'converted to array'] | |
| * // make_folder; 'folder', | |
| * // write_file: 'filename' | |
| * // email_link: { file: 'filename', email: 'user@example.com' } | |
| * // } | |
| * } | |
| * catch (err) { | |
| * console.log(err); | |
| * } | |
| * } | |
| * | |
| */ | |
| function auto(tasks, concurrency, callback) { | |
| if (typeof concurrency !== 'number') { | |
| // concurrency is optional, shift the args. | |
| callback = concurrency; | |
| concurrency = null; | |
| } | |
| callback = (0, _once2.default)(callback || (0, _promiseCallback.promiseCallback)()); | |
| var numTasks = Object.keys(tasks).length; | |
| if (!numTasks) { | |
| return callback(null); | |
| } | |
| if (!concurrency) { | |
| concurrency = numTasks; | |
| } | |
| var results = {}; | |
| var runningTasks = 0; | |
| var canceled = false; | |
| var hasError = false; | |
| var listeners = Object.create(null); | |
| var readyTasks = []; | |
| // for cycle detection: | |
| var readyToCheck = []; // tasks that have been identified as reachable | |
| // without the possibility of returning to an ancestor task | |
| var uncheckedDependencies = {}; | |
| Object.keys(tasks).forEach(key => { | |
| var task = tasks[key]; | |
| if (!Array.isArray(task)) { | |
| // no dependencies | |
| enqueueTask(key, [task]); | |
| readyToCheck.push(key); | |
| return; | |
| } | |
| var dependencies = task.slice(0, task.length - 1); | |
| var remainingDependencies = dependencies.length; | |
| if (remainingDependencies === 0) { | |
| enqueueTask(key, task); | |
| readyToCheck.push(key); | |
| return; | |
| } | |
| uncheckedDependencies[key] = remainingDependencies; | |
| dependencies.forEach(dependencyName => { | |
| if (!tasks[dependencyName]) { | |
| throw new Error('async.auto task `' + key + '` has a non-existent dependency `' + dependencyName + '` in ' + dependencies.join(', ')); | |
| } | |
| addListener(dependencyName, () => { | |
| remainingDependencies--; | |
| if (remainingDependencies === 0) { | |
| enqueueTask(key, task); | |
| } | |
| }); | |
| }); | |
| }); | |
| checkForDeadlocks(); | |
| processQueue(); | |
| function enqueueTask(key, task) { | |
| readyTasks.push(() => runTask(key, task)); | |
| } | |
| function processQueue() { | |
| if (canceled) return; | |
| if (readyTasks.length === 0 && runningTasks === 0) { | |
| return callback(null, results); | |
| } | |
| while (readyTasks.length && runningTasks < concurrency) { | |
| var run = readyTasks.shift(); | |
| run(); | |
| } | |
| } | |
| function addListener(taskName, fn) { | |
| var taskListeners = listeners[taskName]; | |
| if (!taskListeners) { | |
| taskListeners = listeners[taskName] = []; | |
| } | |
| taskListeners.push(fn); | |
| } | |
| function taskComplete(taskName) { | |
| var taskListeners = listeners[taskName] || []; | |
| taskListeners.forEach(fn => fn()); | |
| processQueue(); | |
| } | |
| function runTask(key, task) { | |
| if (hasError) return; | |
| var taskCallback = (0, _onlyOnce2.default)((err, ...result) => { | |
| runningTasks--; | |
| if (err === false) { | |
| canceled = true; | |
| return; | |
| } | |
| if (result.length < 2) { | |
| [result] = result; | |
| } | |
| if (err) { | |
| var safeResults = {}; | |
| Object.keys(results).forEach(rkey => { | |
| safeResults[rkey] = results[rkey]; | |
| }); | |
| safeResults[key] = result; | |
| hasError = true; | |
| listeners = Object.create(null); | |
| if (canceled) return; | |
| callback(err, safeResults); | |
| } else { | |
| results[key] = result; | |
| taskComplete(key); | |
| } | |
| }); | |
| runningTasks++; | |
| var taskFn = (0, _wrapAsync2.default)(task[task.length - 1]); | |
| if (task.length > 1) { | |
| taskFn(results, taskCallback); | |
| } else { | |
| taskFn(taskCallback); | |
| } | |
| } | |
| function checkForDeadlocks() { | |
| // Kahn's algorithm | |
| // https://en.wikipedia.org/wiki/Topological_sorting#Kahn.27s_algorithm | |
| // http://connalle.blogspot.com/2013/10/topological-sortingkahn-algorithm.html | |
| var currentTask; | |
| var counter = 0; | |
| while (readyToCheck.length) { | |
| currentTask = readyToCheck.pop(); | |
| counter++; | |
| getDependents(currentTask).forEach(dependent => { | |
| if (--uncheckedDependencies[dependent] === 0) { | |
| readyToCheck.push(dependent); | |
| } | |
| }); | |
| } | |
| if (counter !== numTasks) { | |
| throw new Error('async.auto cannot execute tasks due to a recursive dependency'); | |
| } | |
| } | |
| function getDependents(taskName) { | |
| var result = []; | |
| Object.keys(tasks).forEach(key => { | |
| const task = tasks[key]; | |
| if (Array.isArray(task) && task.indexOf(taskName) >= 0) { | |
| result.push(key); | |
| } | |
| }); | |
| return result; | |
| } | |
| return callback[_promiseCallback.PROMISE_SYMBOL]; | |
| } | |
| module.exports = exports.default; |