blob: 5b277cd20cb03b462c463b1a9c7b496979aa3e06 [file] [log] [blame]
Paul Greyson6f86d1e2013-03-18 14:40:39 -07001/*global setImmediate: false, setTimeout: false, console: false */
2(function () {
3
4 var async = {};
5
6 // global on the server, window in the browser
7 var root, previous_async;
8
9 root = this;
10 if (root != null) {
11 previous_async = root.async;
12 }
13
14 async.noConflict = function () {
15 root.async = previous_async;
16 return async;
17 };
18
19 function only_once(fn) {
20 var called = false;
21 return function() {
22 if (called) throw new Error("Callback was already called.");
23 called = true;
24 fn.apply(root, arguments);
25 }
26 }
27
28 //// cross-browser compatiblity functions ////
29
30 var _each = function (arr, iterator) {
31 if (arr.forEach) {
32 return arr.forEach(iterator);
33 }
34 for (var i = 0; i < arr.length; i += 1) {
35 iterator(arr[i], i, arr);
36 }
37 };
38
39 var _map = function (arr, iterator) {
40 if (arr.map) {
41 return arr.map(iterator);
42 }
43 var results = [];
44 _each(arr, function (x, i, a) {
45 results.push(iterator(x, i, a));
46 });
47 return results;
48 };
49
50 var _reduce = function (arr, iterator, memo) {
51 if (arr.reduce) {
52 return arr.reduce(iterator, memo);
53 }
54 _each(arr, function (x, i, a) {
55 memo = iterator(memo, x, i, a);
56 });
57 return memo;
58 };
59
60 var _keys = function (obj) {
61 if (Object.keys) {
62 return Object.keys(obj);
63 }
64 var keys = [];
65 for (var k in obj) {
66 if (obj.hasOwnProperty(k)) {
67 keys.push(k);
68 }
69 }
70 return keys;
71 };
72
73 //// exported async module functions ////
74
75 //// nextTick implementation with browser-compatible fallback ////
76 if (typeof process === 'undefined' || !(process.nextTick)) {
77 if (typeof setImmediate === 'function') {
78 async.setImmediate = setImmediate;
79 async.nextTick = setImmediate;
80 }
81 else {
82 async.setImmediate = async.nextTick;
83 async.nextTick = function (fn) {
84 setTimeout(fn, 0);
85 };
86 }
87 }
88 else {
89 async.nextTick = process.nextTick;
90 async.setImmediate = setImmediate;
91 }
92
93 async.each = function (arr, iterator, callback) {
94 callback = callback || function () {};
95 if (!arr.length) {
96 return callback();
97 }
98 var completed = 0;
99 _each(arr, function (x) {
100 iterator(x, only_once(function (err) {
101 if (err) {
102 callback(err);
103 callback = function () {};
104 }
105 else {
106 completed += 1;
107 if (completed >= arr.length) {
108 callback(null);
109 }
110 }
111 }));
112 });
113 };
114 async.forEach = async.each;
115
116 async.eachSeries = function (arr, iterator, callback) {
117 callback = callback || function () {};
118 if (!arr.length) {
119 return callback();
120 }
121 var completed = 0;
122 var iterate = function () {
123 iterator(arr[completed], function (err) {
124 if (err) {
125 callback(err);
126 callback = function () {};
127 }
128 else {
129 completed += 1;
130 if (completed >= arr.length) {
131 callback(null);
132 }
133 else {
134 iterate();
135 }
136 }
137 });
138 };
139 iterate();
140 };
141 async.forEachSeries = async.eachSeries;
142
143 async.eachLimit = function (arr, limit, iterator, callback) {
144 var fn = _eachLimit(limit);
145 fn.apply(null, [arr, iterator, callback]);
146 };
147 async.forEachLimit = async.eachLimit;
148
149 var _eachLimit = function (limit) {
150
151 return function (arr, iterator, callback) {
152 callback = callback || function () {};
153 if (!arr.length || limit <= 0) {
154 return callback();
155 }
156 var completed = 0;
157 var started = 0;
158 var running = 0;
159
160 (function replenish () {
161 if (completed >= arr.length) {
162 return callback();
163 }
164
165 while (running < limit && started < arr.length) {
166 started += 1;
167 running += 1;
168 iterator(arr[started - 1], function (err) {
169 if (err) {
170 callback(err);
171 callback = function () {};
172 }
173 else {
174 completed += 1;
175 running -= 1;
176 if (completed >= arr.length) {
177 callback();
178 }
179 else {
180 replenish();
181 }
182 }
183 });
184 }
185 })();
186 };
187 };
188
189
190 var doParallel = function (fn) {
191 return function () {
192 var args = Array.prototype.slice.call(arguments);
193 return fn.apply(null, [async.each].concat(args));
194 };
195 };
196 var doParallelLimit = function(limit, fn) {
197 return function () {
198 var args = Array.prototype.slice.call(arguments);
199 return fn.apply(null, [_eachLimit(limit)].concat(args));
200 };
201 };
202 var doSeries = function (fn) {
203 return function () {
204 var args = Array.prototype.slice.call(arguments);
205 return fn.apply(null, [async.eachSeries].concat(args));
206 };
207 };
208
209
210 var _asyncMap = function (eachfn, arr, iterator, callback) {
211 var results = [];
212 arr = _map(arr, function (x, i) {
213 return {index: i, value: x};
214 });
215 eachfn(arr, function (x, callback) {
216 iterator(x.value, function (err, v) {
217 results[x.index] = v;
218 callback(err);
219 });
220 }, function (err) {
221 callback(err, results);
222 });
223 };
224 async.map = doParallel(_asyncMap);
225 async.mapSeries = doSeries(_asyncMap);
226 async.mapLimit = function (arr, limit, iterator, callback) {
227 return _mapLimit(limit)(arr, iterator, callback);
228 };
229
230 var _mapLimit = function(limit) {
231 return doParallelLimit(limit, _asyncMap);
232 };
233
234 // reduce only has a series version, as doing reduce in parallel won't
235 // work in many situations.
236 async.reduce = function (arr, memo, iterator, callback) {
237 async.eachSeries(arr, function (x, callback) {
238 iterator(memo, x, function (err, v) {
239 memo = v;
240 callback(err);
241 });
242 }, function (err) {
243 callback(err, memo);
244 });
245 };
246 // inject alias
247 async.inject = async.reduce;
248 // foldl alias
249 async.foldl = async.reduce;
250
251 async.reduceRight = function (arr, memo, iterator, callback) {
252 var reversed = _map(arr, function (x) {
253 return x;
254 }).reverse();
255 async.reduce(reversed, memo, iterator, callback);
256 };
257 // foldr alias
258 async.foldr = async.reduceRight;
259
260 var _filter = function (eachfn, arr, iterator, callback) {
261 var results = [];
262 arr = _map(arr, function (x, i) {
263 return {index: i, value: x};
264 });
265 eachfn(arr, function (x, callback) {
266 iterator(x.value, function (v) {
267 if (v) {
268 results.push(x);
269 }
270 callback();
271 });
272 }, function (err) {
273 callback(_map(results.sort(function (a, b) {
274 return a.index - b.index;
275 }), function (x) {
276 return x.value;
277 }));
278 });
279 };
280 async.filter = doParallel(_filter);
281 async.filterSeries = doSeries(_filter);
282 // select alias
283 async.select = async.filter;
284 async.selectSeries = async.filterSeries;
285
286 var _reject = function (eachfn, arr, iterator, callback) {
287 var results = [];
288 arr = _map(arr, function (x, i) {
289 return {index: i, value: x};
290 });
291 eachfn(arr, function (x, callback) {
292 iterator(x.value, function (v) {
293 if (!v) {
294 results.push(x);
295 }
296 callback();
297 });
298 }, function (err) {
299 callback(_map(results.sort(function (a, b) {
300 return a.index - b.index;
301 }), function (x) {
302 return x.value;
303 }));
304 });
305 };
306 async.reject = doParallel(_reject);
307 async.rejectSeries = doSeries(_reject);
308
309 var _detect = function (eachfn, arr, iterator, main_callback) {
310 eachfn(arr, function (x, callback) {
311 iterator(x, function (result) {
312 if (result) {
313 main_callback(x);
314 main_callback = function () {};
315 }
316 else {
317 callback();
318 }
319 });
320 }, function (err) {
321 main_callback();
322 });
323 };
324 async.detect = doParallel(_detect);
325 async.detectSeries = doSeries(_detect);
326
327 async.some = function (arr, iterator, main_callback) {
328 async.each(arr, function (x, callback) {
329 iterator(x, function (v) {
330 if (v) {
331 main_callback(true);
332 main_callback = function () {};
333 }
334 callback();
335 });
336 }, function (err) {
337 main_callback(false);
338 });
339 };
340 // any alias
341 async.any = async.some;
342
343 async.every = function (arr, iterator, main_callback) {
344 async.each(arr, function (x, callback) {
345 iterator(x, function (v) {
346 if (!v) {
347 main_callback(false);
348 main_callback = function () {};
349 }
350 callback();
351 });
352 }, function (err) {
353 main_callback(true);
354 });
355 };
356 // all alias
357 async.all = async.every;
358
359 async.sortBy = function (arr, iterator, callback) {
360 async.map(arr, function (x, callback) {
361 iterator(x, function (err, criteria) {
362 if (err) {
363 callback(err);
364 }
365 else {
366 callback(null, {value: x, criteria: criteria});
367 }
368 });
369 }, function (err, results) {
370 if (err) {
371 return callback(err);
372 }
373 else {
374 var fn = function (left, right) {
375 var a = left.criteria, b = right.criteria;
376 return a < b ? -1 : a > b ? 1 : 0;
377 };
378 callback(null, _map(results.sort(fn), function (x) {
379 return x.value;
380 }));
381 }
382 });
383 };
384
385 async.auto = function (tasks, callback) {
386 callback = callback || function () {};
387 var keys = _keys(tasks);
388 if (!keys.length) {
389 return callback(null);
390 }
391
392 var results = {};
393
394 var listeners = [];
395 var addListener = function (fn) {
396 listeners.unshift(fn);
397 };
398 var removeListener = function (fn) {
399 for (var i = 0; i < listeners.length; i += 1) {
400 if (listeners[i] === fn) {
401 listeners.splice(i, 1);
402 return;
403 }
404 }
405 };
406 var taskComplete = function () {
407 _each(listeners.slice(0), function (fn) {
408 fn();
409 });
410 };
411
412 addListener(function () {
413 if (_keys(results).length === keys.length) {
414 callback(null, results);
415 callback = function () {};
416 }
417 });
418
419 _each(keys, function (k) {
420 var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
421 var taskCallback = function (err) {
422 var args = Array.prototype.slice.call(arguments, 1);
423 if (args.length <= 1) {
424 args = args[0];
425 }
426 if (err) {
427 var safeResults = {};
428 _each(_keys(results), function(rkey) {
429 safeResults[rkey] = results[rkey];
430 });
431 safeResults[k] = args;
432 callback(err, safeResults);
433 // stop subsequent errors hitting callback multiple times
434 callback = function () {};
435 }
436 else {
437 results[k] = args;
438 async.setImmediate(taskComplete);
439 }
440 };
441 var requires = task.slice(0, Math.abs(task.length - 1)) || [];
442 var ready = function () {
443 return _reduce(requires, function (a, x) {
444 return (a && results.hasOwnProperty(x));
445 }, true) && !results.hasOwnProperty(k);
446 };
447 if (ready()) {
448 task[task.length - 1](taskCallback, results);
449 }
450 else {
451 var listener = function () {
452 if (ready()) {
453 removeListener(listener);
454 task[task.length - 1](taskCallback, results);
455 }
456 };
457 addListener(listener);
458 }
459 });
460 };
461
462 async.waterfall = function (tasks, callback) {
463 callback = callback || function () {};
464 if (tasks.constructor !== Array) {
465 var err = new Error('First argument to waterfall must be an array of functions');
466 return callback(err);
467 }
468 if (!tasks.length) {
469 return callback();
470 }
471 var wrapIterator = function (iterator) {
472 return function (err) {
473 if (err) {
474 callback.apply(null, arguments);
475 callback = function () {};
476 }
477 else {
478 var args = Array.prototype.slice.call(arguments, 1);
479 var next = iterator.next();
480 if (next) {
481 args.push(wrapIterator(next));
482 }
483 else {
484 args.push(callback);
485 }
486 async.setImmediate(function () {
487 iterator.apply(null, args);
488 });
489 }
490 };
491 };
492 wrapIterator(async.iterator(tasks))();
493 };
494
495 var _parallel = function(eachfn, tasks, callback) {
496 callback = callback || function () {};
497 if (tasks.constructor === Array) {
498 eachfn.map(tasks, function (fn, callback) {
499 if (fn) {
500 fn(function (err) {
501 var args = Array.prototype.slice.call(arguments, 1);
502 if (args.length <= 1) {
503 args = args[0];
504 }
505 callback.call(null, err, args);
506 });
507 }
508 }, callback);
509 }
510 else {
511 var results = {};
512 eachfn.each(_keys(tasks), function (k, callback) {
513 tasks[k](function (err) {
514 var args = Array.prototype.slice.call(arguments, 1);
515 if (args.length <= 1) {
516 args = args[0];
517 }
518 results[k] = args;
519 callback(err);
520 });
521 }, function (err) {
522 callback(err, results);
523 });
524 }
525 };
526
527 async.parallel = function (tasks, callback) {
528 _parallel({ map: async.map, each: async.each }, tasks, callback);
529 };
530
531 async.parallelLimit = function(tasks, limit, callback) {
532 _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
533 };
534
535 async.series = function (tasks, callback) {
536 callback = callback || function () {};
537 if (tasks.constructor === Array) {
538 async.mapSeries(tasks, function (fn, callback) {
539 if (fn) {
540 fn(function (err) {
541 var args = Array.prototype.slice.call(arguments, 1);
542 if (args.length <= 1) {
543 args = args[0];
544 }
545 callback.call(null, err, args);
546 });
547 }
548 }, callback);
549 }
550 else {
551 var results = {};
552 async.eachSeries(_keys(tasks), function (k, callback) {
553 tasks[k](function (err) {
554 var args = Array.prototype.slice.call(arguments, 1);
555 if (args.length <= 1) {
556 args = args[0];
557 }
558 results[k] = args;
559 callback(err);
560 });
561 }, function (err) {
562 callback(err, results);
563 });
564 }
565 };
566
567 async.iterator = function (tasks) {
568 var makeCallback = function (index) {
569 var fn = function () {
570 if (tasks.length) {
571 tasks[index].apply(null, arguments);
572 }
573 return fn.next();
574 };
575 fn.next = function () {
576 return (index < tasks.length - 1) ? makeCallback(index + 1): null;
577 };
578 return fn;
579 };
580 return makeCallback(0);
581 };
582
583 async.apply = function (fn) {
584 var args = Array.prototype.slice.call(arguments, 1);
585 return function () {
586 return fn.apply(
587 null, args.concat(Array.prototype.slice.call(arguments))
588 );
589 };
590 };
591
592 var _concat = function (eachfn, arr, fn, callback) {
593 var r = [];
594 eachfn(arr, function (x, cb) {
595 fn(x, function (err, y) {
596 r = r.concat(y || []);
597 cb(err);
598 });
599 }, function (err) {
600 callback(err, r);
601 });
602 };
603 async.concat = doParallel(_concat);
604 async.concatSeries = doSeries(_concat);
605
606 async.whilst = function (test, iterator, callback) {
607 if (test()) {
608 iterator(function (err) {
609 if (err) {
610 return callback(err);
611 }
612 async.whilst(test, iterator, callback);
613 });
614 }
615 else {
616 callback();
617 }
618 };
619
620 async.doWhilst = function (iterator, test, callback) {
621 iterator(function (err) {
622 if (err) {
623 return callback(err);
624 }
625 if (test()) {
626 async.doWhilst(iterator, test, callback);
627 }
628 else {
629 callback();
630 }
631 });
632 };
633
634 async.until = function (test, iterator, callback) {
635 if (!test()) {
636 iterator(function (err) {
637 if (err) {
638 return callback(err);
639 }
640 async.until(test, iterator, callback);
641 });
642 }
643 else {
644 callback();
645 }
646 };
647
648 async.doUntil = function (iterator, test, callback) {
649 iterator(function (err) {
650 if (err) {
651 return callback(err);
652 }
653 if (!test()) {
654 async.doUntil(iterator, test, callback);
655 }
656 else {
657 callback();
658 }
659 });
660 };
661
662 async.queue = function (worker, concurrency) {
663 if (concurrency === undefined) {
664 concurrency = 1;
665 }
666 function _insert(q, data, pos, callback) {
667 if(data.constructor !== Array) {
668 data = [data];
669 }
670 _each(data, function(task) {
671 var item = {
672 data: task,
673 callback: typeof callback === 'function' ? callback : null
674 };
675
676 if (pos) {
677 q.tasks.unshift(item);
678 } else {
679 q.tasks.push(item);
680 }
681
682 if (q.saturated && q.tasks.length === concurrency) {
683 q.saturated();
684 }
685 async.setImmediate(q.process);
686 });
687 }
688
689 var workers = 0;
690 var q = {
691 tasks: [],
692 concurrency: concurrency,
693 saturated: null,
694 empty: null,
695 drain: null,
696 push: function (data, callback) {
697 _insert(q, data, false, callback);
698 },
699 unshift: function (data, callback) {
700 _insert(q, data, true, callback);
701 },
702 process: function () {
703 if (workers < q.concurrency && q.tasks.length) {
704 var task = q.tasks.shift();
705 if (q.empty && q.tasks.length === 0) {
706 q.empty();
707 }
708 workers += 1;
709 var next = function () {
710 workers -= 1;
711 if (task.callback) {
712 task.callback.apply(task, arguments);
713 }
714 if (q.drain && q.tasks.length + workers === 0) {
715 q.drain();
716 }
717 q.process();
718 };
719 var cb = only_once(next);
720 worker(task.data, cb);
721 }
722 },
723 length: function () {
724 return q.tasks.length;
725 },
726 running: function () {
727 return workers;
728 }
729 };
730 return q;
731 };
732
733 async.cargo = function (worker, payload) {
734 var working = false,
735 tasks = [];
736
737 var cargo = {
738 tasks: tasks,
739 payload: payload,
740 saturated: null,
741 empty: null,
742 drain: null,
743 push: function (data, callback) {
744 if(data.constructor !== Array) {
745 data = [data];
746 }
747 _each(data, function(task) {
748 tasks.push({
749 data: task,
750 callback: typeof callback === 'function' ? callback : null
751 });
752 if (cargo.saturated && tasks.length === payload) {
753 cargo.saturated();
754 }
755 });
756 async.setImmediate(cargo.process);
757 },
758 process: function process() {
759 if (working) return;
760 if (tasks.length === 0) {
761 if(cargo.drain) cargo.drain();
762 return;
763 }
764
765 var ts = typeof payload === 'number'
766 ? tasks.splice(0, payload)
767 : tasks.splice(0);
768
769 var ds = _map(ts, function (task) {
770 return task.data;
771 });
772
773 if(cargo.empty) cargo.empty();
774 working = true;
775 worker(ds, function () {
776 working = false;
777
778 var args = arguments;
779 _each(ts, function (data) {
780 if (data.callback) {
781 data.callback.apply(null, args);
782 }
783 });
784
785 process();
786 });
787 },
788 length: function () {
789 return tasks.length;
790 },
791 running: function () {
792 return working;
793 }
794 };
795 return cargo;
796 };
797
798 var _console_fn = function (name) {
799 return function (fn) {
800 var args = Array.prototype.slice.call(arguments, 1);
801 fn.apply(null, args.concat([function (err) {
802 var args = Array.prototype.slice.call(arguments, 1);
803 if (typeof console !== 'undefined') {
804 if (err) {
805 if (console.error) {
806 console.error(err);
807 }
808 }
809 else if (console[name]) {
810 _each(args, function (x) {
811 console[name](x);
812 });
813 }
814 }
815 }]));
816 };
817 };
818 async.log = _console_fn('log');
819 async.dir = _console_fn('dir');
820 /*async.info = _console_fn('info');
821 async.warn = _console_fn('warn');
822 async.error = _console_fn('error');*/
823
824 async.memoize = function (fn, hasher) {
825 var memo = {};
826 var queues = {};
827 hasher = hasher || function (x) {
828 return x;
829 };
830 var memoized = function () {
831 var args = Array.prototype.slice.call(arguments);
832 var callback = args.pop();
833 var key = hasher.apply(null, args);
834 if (key in memo) {
835 callback.apply(null, memo[key]);
836 }
837 else if (key in queues) {
838 queues[key].push(callback);
839 }
840 else {
841 queues[key] = [callback];
842 fn.apply(null, args.concat([function () {
843 memo[key] = arguments;
844 var q = queues[key];
845 delete queues[key];
846 for (var i = 0, l = q.length; i < l; i++) {
847 q[i].apply(null, arguments);
848 }
849 }]));
850 }
851 };
852 memoized.memo = memo;
853 memoized.unmemoized = fn;
854 return memoized;
855 };
856
857 async.unmemoize = function (fn) {
858 return function () {
859 return (fn.unmemoized || fn).apply(null, arguments);
860 };
861 };
862
863 async.times = function (count, iterator, callback) {
864 var counter = [];
865 for (var i = 0; i < count; i++) {
866 counter.push(i);
867 }
868 return async.map(counter, iterator, callback);
869 };
870
871 async.timesSeries = function (count, iterator, callback) {
872 var counter = [];
873 for (var i = 0; i < count; i++) {
874 counter.push(i);
875 }
876 return async.mapSeries(counter, iterator, callback);
877 };
878
879 async.compose = function (/* functions... */) {
880 var fns = Array.prototype.reverse.call(arguments);
881 return function () {
882 var that = this;
883 var args = Array.prototype.slice.call(arguments);
884 var callback = args.pop();
885 async.reduce(fns, args, function (newargs, fn, cb) {
886 fn.apply(that, newargs.concat([function () {
887 var err = arguments[0];
888 var nextargs = Array.prototype.slice.call(arguments, 1);
889 cb(err, nextargs);
890 }]))
891 },
892 function (err, results) {
893 callback.apply(that, [err].concat(results));
894 });
895 };
896 };
897
898 var _applyEach = function (eachfn, fns /*args...*/) {
899 var go = function () {
900 var that = this;
901 var args = Array.prototype.slice.call(arguments);
902 var callback = args.pop();
903 return eachfn(fns, function (fn, cb) {
904 fn.apply(that, args.concat([cb]));
905 },
906 callback);
907 };
908 if (arguments.length > 2) {
909 var args = Array.prototype.slice.call(arguments, 2);
910 return go.apply(this, args);
911 }
912 else {
913 return go;
914 }
915 };
916 async.applyEach = doParallel(_applyEach);
917 async.applyEachSeries = doSeries(_applyEach);
918
919 async.forever = function (fn, callback) {
920 function next(err) {
921 if (err) {
922 if (callback) {
923 return callback(err);
924 }
925 throw err;
926 }
927 fn(next);
928 }
929 next();
930 };
931
932 // AMD / RequireJS
933 if (typeof define !== 'undefined' && define.amd) {
934 define([], function () {
935 return async;
936 });
937 }
938 // Node.js
939 else if (typeof module !== 'undefined' && module.exports) {
940 module.exports = async;
941 }
942 // included directly via <script> tag
943 else {
944 root.async = async;
945 }
946
947}());