Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MR414N-ID
GitHub Repository: MR414N-ID/botku2
Path: blob/master/node_modules/async/auto.js
1126 views
1
'use strict';
2
3
Object.defineProperty(exports, "__esModule", {
4
value: true
5
});
6
exports.default = auto;
7
8
var _once = require('./internal/once.js');
9
10
var _once2 = _interopRequireDefault(_once);
11
12
var _onlyOnce = require('./internal/onlyOnce.js');
13
14
var _onlyOnce2 = _interopRequireDefault(_onlyOnce);
15
16
var _wrapAsync = require('./internal/wrapAsync.js');
17
18
var _wrapAsync2 = _interopRequireDefault(_wrapAsync);
19
20
var _promiseCallback = require('./internal/promiseCallback.js');
21
22
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
24
/**
25
* Determines the best order for running the {@link AsyncFunction}s in `tasks`, based on
26
* their requirements. Each function can optionally depend on other functions
27
* being completed first, and each function is run as soon as its requirements
28
* are satisfied.
29
*
30
* If any of the {@link AsyncFunction}s pass an error to their callback, the `auto` sequence
31
* will stop. Further tasks will not execute (so any other functions depending
32
* on it will not run), and the main `callback` is immediately called with the
33
* error.
34
*
35
* {@link AsyncFunction}s also receive an object containing the results of functions which
36
* have completed so far as the first argument, if they have dependencies. If a
37
* task function has no dependencies, it will only be passed a callback.
38
*
39
* @name auto
40
* @static
41
* @memberOf module:ControlFlow
42
* @method
43
* @category Control Flow
44
* @param {Object} tasks - An object. Each of its properties is either a
45
* function or an array of requirements, with the {@link AsyncFunction} itself the last item
46
* in the array. The object's key of a property serves as the name of the task
47
* defined by that property, i.e. can be used when specifying requirements for
48
* other tasks. The function receives one or two arguments:
49
* * a `results` object, containing the results of the previously executed
50
* functions, only passed if the task has any dependencies,
51
* * a `callback(err, result)` function, which must be called when finished,
52
* passing an `error` (which can be `null`) and the result of the function's
53
* execution.
54
* @param {number} [concurrency=Infinity] - An optional `integer` for
55
* determining the maximum number of tasks that can be run in parallel. By
56
* default, as many as possible.
57
* @param {Function} [callback] - An optional callback which is called when all
58
* the tasks have been completed. It receives the `err` argument if any `tasks`
59
* pass an error to their callback. Results are always returned; however, if an
60
* error occurs, no further `tasks` will be performed, and the results object
61
* will only contain partial results. Invoked with (err, results).
62
* @returns {Promise} a promise, if a callback is not passed
63
* @example
64
*
65
* //Using Callbacks
66
* async.auto({
67
* get_data: function(callback) {
68
* // async code to get some data
69
* callback(null, 'data', 'converted to array');
70
* },
71
* make_folder: function(callback) {
72
* // async code to create a directory to store a file in
73
* // this is run at the same time as getting the data
74
* callback(null, 'folder');
75
* },
76
* write_file: ['get_data', 'make_folder', function(results, callback) {
77
* // once there is some data and the directory exists,
78
* // write the data to a file in the directory
79
* callback(null, 'filename');
80
* }],
81
* email_link: ['write_file', function(results, callback) {
82
* // once the file is written let's email a link to it...
83
* callback(null, {'file':results.write_file, 'email':'[email protected]'});
84
* }]
85
* }, function(err, results) {
86
* if (err) {
87
* console.log('err = ', err);
88
* }
89
* console.log('results = ', results);
90
* // results = {
91
* // get_data: ['data', 'converted to array']
92
* // make_folder; 'folder',
93
* // write_file: 'filename'
94
* // email_link: { file: 'filename', email: '[email protected]' }
95
* // }
96
* });
97
*
98
* //Using Promises
99
* async.auto({
100
* get_data: function(callback) {
101
* console.log('in get_data');
102
* // async code to get some data
103
* callback(null, 'data', 'converted to array');
104
* },
105
* make_folder: function(callback) {
106
* console.log('in make_folder');
107
* // async code to create a directory to store a file in
108
* // this is run at the same time as getting the data
109
* callback(null, 'folder');
110
* },
111
* write_file: ['get_data', 'make_folder', function(results, callback) {
112
* // once there is some data and the directory exists,
113
* // write the data to a file in the directory
114
* callback(null, 'filename');
115
* }],
116
* email_link: ['write_file', function(results, callback) {
117
* // once the file is written let's email a link to it...
118
* callback(null, {'file':results.write_file, 'email':'[email protected]'});
119
* }]
120
* }).then(results => {
121
* console.log('results = ', results);
122
* // results = {
123
* // get_data: ['data', 'converted to array']
124
* // make_folder; 'folder',
125
* // write_file: 'filename'
126
* // email_link: { file: 'filename', email: '[email protected]' }
127
* // }
128
* }).catch(err => {
129
* console.log('err = ', err);
130
* });
131
*
132
* //Using async/await
133
* async () => {
134
* try {
135
* let results = await async.auto({
136
* get_data: function(callback) {
137
* // async code to get some data
138
* callback(null, 'data', 'converted to array');
139
* },
140
* make_folder: function(callback) {
141
* // async code to create a directory to store a file in
142
* // this is run at the same time as getting the data
143
* callback(null, 'folder');
144
* },
145
* write_file: ['get_data', 'make_folder', function(results, callback) {
146
* // once there is some data and the directory exists,
147
* // write the data to a file in the directory
148
* callback(null, 'filename');
149
* }],
150
* email_link: ['write_file', function(results, callback) {
151
* // once the file is written let's email a link to it...
152
* callback(null, {'file':results.write_file, 'email':'[email protected]'});
153
* }]
154
* });
155
* console.log('results = ', results);
156
* // results = {
157
* // get_data: ['data', 'converted to array']
158
* // make_folder; 'folder',
159
* // write_file: 'filename'
160
* // email_link: { file: 'filename', email: '[email protected]' }
161
* // }
162
* }
163
* catch (err) {
164
* console.log(err);
165
* }
166
* }
167
*
168
*/
169
function auto(tasks, concurrency, callback) {
170
if (typeof concurrency !== 'number') {
171
// concurrency is optional, shift the args.
172
callback = concurrency;
173
concurrency = null;
174
}
175
callback = (0, _once2.default)(callback || (0, _promiseCallback.promiseCallback)());
176
var numTasks = Object.keys(tasks).length;
177
if (!numTasks) {
178
return callback(null);
179
}
180
if (!concurrency) {
181
concurrency = numTasks;
182
}
183
184
var results = {};
185
var runningTasks = 0;
186
var canceled = false;
187
var hasError = false;
188
189
var listeners = Object.create(null);
190
191
var readyTasks = [];
192
193
// for cycle detection:
194
var readyToCheck = []; // tasks that have been identified as reachable
195
// without the possibility of returning to an ancestor task
196
var uncheckedDependencies = {};
197
198
Object.keys(tasks).forEach(key => {
199
var task = tasks[key];
200
if (!Array.isArray(task)) {
201
// no dependencies
202
enqueueTask(key, [task]);
203
readyToCheck.push(key);
204
return;
205
}
206
207
var dependencies = task.slice(0, task.length - 1);
208
var remainingDependencies = dependencies.length;
209
if (remainingDependencies === 0) {
210
enqueueTask(key, task);
211
readyToCheck.push(key);
212
return;
213
}
214
uncheckedDependencies[key] = remainingDependencies;
215
216
dependencies.forEach(dependencyName => {
217
if (!tasks[dependencyName]) {
218
throw new Error('async.auto task `' + key + '` has a non-existent dependency `' + dependencyName + '` in ' + dependencies.join(', '));
219
}
220
addListener(dependencyName, () => {
221
remainingDependencies--;
222
if (remainingDependencies === 0) {
223
enqueueTask(key, task);
224
}
225
});
226
});
227
});
228
229
checkForDeadlocks();
230
processQueue();
231
232
function enqueueTask(key, task) {
233
readyTasks.push(() => runTask(key, task));
234
}
235
236
function processQueue() {
237
if (canceled) return;
238
if (readyTasks.length === 0 && runningTasks === 0) {
239
return callback(null, results);
240
}
241
while (readyTasks.length && runningTasks < concurrency) {
242
var run = readyTasks.shift();
243
run();
244
}
245
}
246
247
function addListener(taskName, fn) {
248
var taskListeners = listeners[taskName];
249
if (!taskListeners) {
250
taskListeners = listeners[taskName] = [];
251
}
252
253
taskListeners.push(fn);
254
}
255
256
function taskComplete(taskName) {
257
var taskListeners = listeners[taskName] || [];
258
taskListeners.forEach(fn => fn());
259
processQueue();
260
}
261
262
function runTask(key, task) {
263
if (hasError) return;
264
265
var taskCallback = (0, _onlyOnce2.default)((err, ...result) => {
266
runningTasks--;
267
if (err === false) {
268
canceled = true;
269
return;
270
}
271
if (result.length < 2) {
272
[result] = result;
273
}
274
if (err) {
275
var safeResults = {};
276
Object.keys(results).forEach(rkey => {
277
safeResults[rkey] = results[rkey];
278
});
279
safeResults[key] = result;
280
hasError = true;
281
listeners = Object.create(null);
282
if (canceled) return;
283
callback(err, safeResults);
284
} else {
285
results[key] = result;
286
taskComplete(key);
287
}
288
});
289
290
runningTasks++;
291
var taskFn = (0, _wrapAsync2.default)(task[task.length - 1]);
292
if (task.length > 1) {
293
taskFn(results, taskCallback);
294
} else {
295
taskFn(taskCallback);
296
}
297
}
298
299
function checkForDeadlocks() {
300
// Kahn's algorithm
301
// https://en.wikipedia.org/wiki/Topological_sorting#Kahn.27s_algorithm
302
// http://connalle.blogspot.com/2013/10/topological-sortingkahn-algorithm.html
303
var currentTask;
304
var counter = 0;
305
while (readyToCheck.length) {
306
currentTask = readyToCheck.pop();
307
counter++;
308
getDependents(currentTask).forEach(dependent => {
309
if (--uncheckedDependencies[dependent] === 0) {
310
readyToCheck.push(dependent);
311
}
312
});
313
}
314
315
if (counter !== numTasks) {
316
throw new Error('async.auto cannot execute tasks due to a recursive dependency');
317
}
318
}
319
320
function getDependents(taskName) {
321
var result = [];
322
Object.keys(tasks).forEach(key => {
323
const task = tasks[key];
324
if (Array.isArray(task) && task.indexOf(taskName) >= 0) {
325
result.push(key);
326
}
327
});
328
return result;
329
}
330
331
return callback[_promiseCallback.PROMISE_SYMBOL];
332
}
333
module.exports = exports['default'];
334