├── .gitignore
├── .npmignore
├── nodelint.cfg
├── index.js
├── .gitmodules
├── package.json
├── Makefile
├── test
├── test.html
└── test-async.js
├── LICENSE
├── deps
├── nodeunit.css
└── nodeunit.js
├── dist
└── async.min.js
├── lib
└── async.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | deps
2 | dist
3 | test
4 | nodelint.cfg
--------------------------------------------------------------------------------
/nodelint.cfg:
--------------------------------------------------------------------------------
1 | var options = {
2 | indent: 4,
3 | onevar: false
4 | };
5 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // This file is just added for convenience so this repository can be
2 | // directly checked out into a project's deps folder
3 | module.exports = require('./lib/async');
4 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "deps/nodeunit"]
2 | path = deps/nodeunit
3 | url = git://github.com/caolan/nodeunit.git
4 | [submodule "deps/UglifyJS"]
5 | path = deps/UglifyJS
6 | url = https://github.com/mishoo/UglifyJS.git
7 | [submodule "deps/nodelint"]
8 | path = deps/nodelint
9 | url = https://github.com/tav/nodelint.git
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | { "name": "async"
2 | , "description": "Higher-order functions and common patterns for asynchronous code"
3 | , "main": "./index"
4 | , "author": "Caolan McMahon"
5 | , "version": "0.1.18"
6 | , "repository" :
7 | { "type" : "git"
8 | , "url" : "http://github.com/caolan/async.git"
9 | }
10 | , "bugs" : { "url" : "http://github.com/caolan/async/issues" }
11 | , "licenses" :
12 | [ { "type" : "MIT"
13 | , "url" : "http://github.com/caolan/async/raw/master/LICENSE"
14 | }
15 | ]
16 | , "devDependencies":
17 | { "nodeunit": ">0.0.0"
18 | , "uglify-js": "1.2.x"
19 | , "nodelint": ">0.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | PACKAGE = asyncjs
2 | NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node)
3 | CWD := $(shell pwd)
4 | NODEUNIT = $(CWD)/node_modules/nodeunit/bin/nodeunit
5 | UGLIFY = $(CWD)/node_modules/uglify-js/bin/uglifyjs
6 | NODELINT = $(CWD)/node_modules/nodelint/nodelint
7 |
8 | BUILDDIR = dist
9 |
10 | all: clean test build
11 |
12 | build: $(wildcard lib/*.js)
13 | mkdir -p $(BUILDDIR)
14 | $(UGLIFY) lib/async.js > $(BUILDDIR)/async.min.js
15 |
16 | test:
17 | $(NODEUNIT) test
18 |
19 | clean:
20 | rm -rf $(BUILDDIR)
21 |
22 | lint:
23 | $(NODELINT) --config nodelint.cfg lib/async.js
24 |
25 | .PHONY: test build all
26 |
--------------------------------------------------------------------------------
/test/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Async.js Test Suite
4 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010 Caolan McMahon
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/deps/nodeunit.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Styles taken from qunit.css
3 | */
4 |
5 | h1#nodeunit-header, h1.nodeunit-header {
6 | padding: 15px;
7 | font-size: large;
8 | background-color: #06b;
9 | color: white;
10 | font-family: 'trebuchet ms', verdana, arial;
11 | margin: 0;
12 | }
13 |
14 | h1#nodeunit-header a {
15 | color: white;
16 | }
17 |
18 | h2#nodeunit-banner {
19 | height: 2em;
20 | border-bottom: 1px solid white;
21 | background-color: #eee;
22 | margin: 0;
23 | font-family: 'trebuchet ms', verdana, arial;
24 | }
25 | h2#nodeunit-banner.pass {
26 | background-color: green;
27 | }
28 | h2#nodeunit-banner.fail {
29 | background-color: red;
30 | }
31 |
32 | h2#nodeunit-userAgent, h2.nodeunit-userAgent {
33 | padding: 10px;
34 | background-color: #eee;
35 | color: black;
36 | margin: 0;
37 | font-size: small;
38 | font-weight: normal;
39 | font-family: 'trebuchet ms', verdana, arial;
40 | font-size: 10pt;
41 | }
42 |
43 | div#nodeunit-testrunner-toolbar {
44 | background: #eee;
45 | border-top: 1px solid black;
46 | padding: 10px;
47 | font-family: 'trebuchet ms', verdana, arial;
48 | margin: 0;
49 | font-size: 10pt;
50 | }
51 |
52 | ol#nodeunit-tests {
53 | font-family: 'trebuchet ms', verdana, arial;
54 | font-size: 10pt;
55 | }
56 | ol#nodeunit-tests li strong {
57 | cursor:pointer;
58 | }
59 | ol#nodeunit-tests .pass {
60 | color: green;
61 | }
62 | ol#nodeunit-tests .fail {
63 | color: red;
64 | }
65 |
66 | p#nodeunit-testresult {
67 | margin-left: 1em;
68 | font-size: 10pt;
69 | font-family: 'trebuchet ms', verdana, arial;
70 | }
71 |
--------------------------------------------------------------------------------
/dist/async.min.js:
--------------------------------------------------------------------------------
1 | /*global setTimeout: false, console: false */(function(){var a={},b=this,c=b.async;typeof module!="undefined"&&module.exports?module.exports=a:b.async=a,a.noConflict=function(){return b.async=c,a};var d=function(a,b){if(a.forEach)return a.forEach(b);for(var c=0;cd?1:0};d(null,e(b.sort(c),function(a){return a.value}))})},a.auto=function(a,b){b=b||function(){};var c=g(a);if(!c.length)return b(null);var e={},h=[],i=function(a){h.unshift(a)},j=function(a){for(var b=0;b b ? 1 : 0;
340 | };
341 | callback(null, _map(results.sort(fn), function (x) {
342 | return x.value;
343 | }));
344 | }
345 | });
346 | };
347 |
348 | async.auto = function (tasks, callback) {
349 | callback = callback || function () {};
350 | var keys = _keys(tasks);
351 | if (!keys.length) {
352 | return callback(null);
353 | }
354 |
355 | var results = {};
356 |
357 | var listeners = [];
358 | var addListener = function (fn) {
359 | listeners.unshift(fn);
360 | };
361 | var removeListener = function (fn) {
362 | for (var i = 0; i < listeners.length; i += 1) {
363 | if (listeners[i] === fn) {
364 | listeners.splice(i, 1);
365 | return;
366 | }
367 | }
368 | };
369 | var taskComplete = function () {
370 | _forEach(listeners.slice(0), function (fn) {
371 | fn();
372 | });
373 | };
374 |
375 | addListener(function () {
376 | if (_keys(results).length === keys.length) {
377 | callback(null, results);
378 | callback = function () {};
379 | }
380 | });
381 |
382 | _forEach(keys, function (k) {
383 | var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
384 | var taskCallback = function (err) {
385 | if (err) {
386 | callback(err);
387 | // stop subsequent errors hitting callback multiple times
388 | callback = function () {};
389 | }
390 | else {
391 | var args = Array.prototype.slice.call(arguments, 1);
392 | if (args.length <= 1) {
393 | args = args[0];
394 | }
395 | results[k] = args;
396 | taskComplete();
397 | }
398 | };
399 | var requires = task.slice(0, Math.abs(task.length - 1)) || [];
400 | var ready = function () {
401 | return _reduce(requires, function (a, x) {
402 | return (a && results.hasOwnProperty(x));
403 | }, true);
404 | };
405 | if (ready()) {
406 | task[task.length - 1](taskCallback, results);
407 | }
408 | else {
409 | var listener = function () {
410 | if (ready()) {
411 | removeListener(listener);
412 | task[task.length - 1](taskCallback, results);
413 | }
414 | };
415 | addListener(listener);
416 | }
417 | });
418 | };
419 |
420 | async.waterfall = function (tasks, callback) {
421 | callback = callback || function () {};
422 | if (!tasks.length) {
423 | return callback();
424 | }
425 | var wrapIterator = function (iterator) {
426 | return function (err) {
427 | if (err) {
428 | callback(err);
429 | callback = function () {};
430 | }
431 | else {
432 | var args = Array.prototype.slice.call(arguments, 1);
433 | var next = iterator.next();
434 | if (next) {
435 | args.push(wrapIterator(next));
436 | }
437 | else {
438 | args.push(callback);
439 | }
440 | async.nextTick(function () {
441 | iterator.apply(null, args);
442 | });
443 | }
444 | };
445 | };
446 | wrapIterator(async.iterator(tasks))();
447 | };
448 |
449 | async.parallel = function (tasks, callback) {
450 | callback = callback || function () {};
451 | if (tasks.constructor === Array) {
452 | async.map(tasks, function (fn, callback) {
453 | if (fn) {
454 | fn(function (err) {
455 | var args = Array.prototype.slice.call(arguments, 1);
456 | if (args.length <= 1) {
457 | args = args[0];
458 | }
459 | callback.call(null, err, args);
460 | });
461 | }
462 | }, callback);
463 | }
464 | else {
465 | var results = {};
466 | async.forEach(_keys(tasks), function (k, callback) {
467 | tasks[k](function (err) {
468 | var args = Array.prototype.slice.call(arguments, 1);
469 | if (args.length <= 1) {
470 | args = args[0];
471 | }
472 | results[k] = args;
473 | callback(err);
474 | });
475 | }, function (err) {
476 | callback(err, results);
477 | });
478 | }
479 | };
480 |
481 | async.series = function (tasks, callback) {
482 | callback = callback || function () {};
483 | if (tasks.constructor === Array) {
484 | async.mapSeries(tasks, function (fn, callback) {
485 | if (fn) {
486 | fn(function (err) {
487 | var args = Array.prototype.slice.call(arguments, 1);
488 | if (args.length <= 1) {
489 | args = args[0];
490 | }
491 | callback.call(null, err, args);
492 | });
493 | }
494 | }, callback);
495 | }
496 | else {
497 | var results = {};
498 | async.forEachSeries(_keys(tasks), function (k, callback) {
499 | tasks[k](function (err) {
500 | var args = Array.prototype.slice.call(arguments, 1);
501 | if (args.length <= 1) {
502 | args = args[0];
503 | }
504 | results[k] = args;
505 | callback(err);
506 | });
507 | }, function (err) {
508 | callback(err, results);
509 | });
510 | }
511 | };
512 |
513 | async.iterator = function (tasks) {
514 | var makeCallback = function (index) {
515 | var fn = function () {
516 | if (tasks.length) {
517 | tasks[index].apply(null, arguments);
518 | }
519 | return fn.next();
520 | };
521 | fn.next = function () {
522 | return (index < tasks.length - 1) ? makeCallback(index + 1): null;
523 | };
524 | return fn;
525 | };
526 | return makeCallback(0);
527 | };
528 |
529 | async.apply = function (fn) {
530 | var args = Array.prototype.slice.call(arguments, 1);
531 | return function () {
532 | return fn.apply(
533 | null, args.concat(Array.prototype.slice.call(arguments))
534 | );
535 | };
536 | };
537 |
538 | var _concat = function (eachfn, arr, fn, callback) {
539 | var r = [];
540 | eachfn(arr, function (x, cb) {
541 | fn(x, function (err, y) {
542 | r = r.concat(y || []);
543 | cb(err);
544 | });
545 | }, function (err) {
546 | callback(err, r);
547 | });
548 | };
549 | async.concat = doParallel(_concat);
550 | async.concatSeries = doSeries(_concat);
551 |
552 | async.whilst = function (test, iterator, callback) {
553 | if (test()) {
554 | iterator(function (err) {
555 | if (err) {
556 | return callback(err);
557 | }
558 | async.whilst(test, iterator, callback);
559 | });
560 | }
561 | else {
562 | callback();
563 | }
564 | };
565 |
566 | async.until = function (test, iterator, callback) {
567 | if (!test()) {
568 | iterator(function (err) {
569 | if (err) {
570 | return callback(err);
571 | }
572 | async.until(test, iterator, callback);
573 | });
574 | }
575 | else {
576 | callback();
577 | }
578 | };
579 |
580 | async.queue = function (worker, concurrency) {
581 | var workers = 0;
582 | var q = {
583 | tasks: [],
584 | concurrency: concurrency,
585 | saturated: null,
586 | empty: null,
587 | drain: null,
588 | push: function (data, callback) {
589 | if(data.constructor !== Array) {
590 | data = [data];
591 | }
592 | _forEach(data, function(task) {
593 | q.tasks.push({
594 | data: task,
595 | callback: typeof callback === 'function' ? callback : null
596 | });
597 | if (q.saturated && q.tasks.length == concurrency) {
598 | q.saturated();
599 | }
600 | async.nextTick(q.process);
601 | });
602 | },
603 | process: function () {
604 | if (workers < q.concurrency && q.tasks.length) {
605 | var task = q.tasks.shift();
606 | if(q.empty && q.tasks.length == 0) q.empty();
607 | workers += 1;
608 | worker(task.data, function () {
609 | workers -= 1;
610 | if (task.callback) {
611 | task.callback.apply(task, arguments);
612 | }
613 | if(q.drain && q.tasks.length + workers == 0) q.drain();
614 | q.process();
615 | });
616 | }
617 | },
618 | length: function () {
619 | return q.tasks.length;
620 | },
621 | running: function () {
622 | return workers;
623 | }
624 | };
625 | return q;
626 | };
627 |
628 | var _console_fn = function (name) {
629 | return function (fn) {
630 | var args = Array.prototype.slice.call(arguments, 1);
631 | fn.apply(null, args.concat([function (err) {
632 | var args = Array.prototype.slice.call(arguments, 1);
633 | if (typeof console !== 'undefined') {
634 | if (err) {
635 | if (console.error) {
636 | console.error(err);
637 | }
638 | }
639 | else if (console[name]) {
640 | _forEach(args, function (x) {
641 | console[name](x);
642 | });
643 | }
644 | }
645 | }]));
646 | };
647 | };
648 | async.log = _console_fn('log');
649 | async.dir = _console_fn('dir');
650 | /*async.info = _console_fn('info');
651 | async.warn = _console_fn('warn');
652 | async.error = _console_fn('error');*/
653 |
654 | async.memoize = function (fn, hasher) {
655 | var memo = {};
656 | var queues = {};
657 | hasher = hasher || function (x) {
658 | return x;
659 | };
660 | var memoized = function () {
661 | var args = Array.prototype.slice.call(arguments);
662 | var callback = args.pop();
663 | var key = hasher.apply(null, args);
664 | if (key in memo) {
665 | callback.apply(null, memo[key]);
666 | }
667 | else if (key in queues) {
668 | queues[key].push(callback);
669 | }
670 | else {
671 | queues[key] = [callback];
672 | fn.apply(null, args.concat([function () {
673 | memo[key] = arguments;
674 | var q = queues[key];
675 | delete queues[key];
676 | for (var i = 0, l = q.length; i < l; i++) {
677 | q[i].apply(null, arguments);
678 | }
679 | }]));
680 | }
681 | };
682 | memoized.unmemoized = fn;
683 | return memoized;
684 | };
685 |
686 | async.unmemoize = function (fn) {
687 | return function () {
688 | return (fn.unmemoized || fn).apply(null, arguments);
689 | }
690 | };
691 |
692 | }());
693 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Async.js
2 |
3 | Async is a utility module which provides straight-forward, powerful functions
4 | for working with asynchronous JavaScript. Although originally designed for
5 | use with [node.js](http://nodejs.org), it can also be used directly in the
6 | browser.
7 |
8 | Async provides around 20 functions that include the usual 'functional'
9 | suspects (map, reduce, filter, forEach…) as well as some common patterns
10 | for asynchronous control flow (parallel, series, waterfall…). All these
11 | functions assume you follow the node.js convention of providing a single
12 | callback as the last argument of your async function.
13 |
14 |
15 | ## Quick Examples
16 |
17 | async.map(['file1','file2','file3'], fs.stat, function(err, results){
18 | // results is now an array of stats for each file
19 | });
20 |
21 | async.filter(['file1','file2','file3'], path.exists, function(results){
22 | // results now equals an array of the existing files
23 | });
24 |
25 | async.parallel([
26 | function(){ ... },
27 | function(){ ... }
28 | ], callback);
29 |
30 | async.series([
31 | function(){ ... },
32 | function(){ ... }
33 | ]);
34 |
35 | There are many more functions available so take a look at the docs below for a
36 | full list. This module aims to be comprehensive, so if you feel anything is
37 | missing please create a GitHub issue for it.
38 |
39 |
40 | ## Download
41 |
42 | Releases are available for download from
43 | [GitHub](http://github.com/caolan/async/downloads).
44 | Alternatively, you can install using Node Package Manager (npm):
45 |
46 | npm install async
47 |
48 |
49 | __Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 17.5kb Uncompressed
50 |
51 | __Production:__ [async.min.js](https://github.com/caolan/async/raw/master/dist/async.min.js) - 1.7kb Packed and Gzipped
52 |
53 |
54 | ## In the Browser
55 |
56 | So far its been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:
57 |
58 |
59 |
66 |
67 |
68 | ## Documentation
69 |
70 | ### Collections
71 |
72 | * [forEach](#forEach)
73 | * [map](#map)
74 | * [filter](#filter)
75 | * [reject](#reject)
76 | * [reduce](#reduce)
77 | * [detect](#detect)
78 | * [sortBy](#sortBy)
79 | * [some](#some)
80 | * [every](#every)
81 | * [concat](#concat)
82 |
83 | ### Control Flow
84 |
85 | * [series](#series)
86 | * [parallel](#parallel)
87 | * [whilst](#whilst)
88 | * [until](#until)
89 | * [waterfall](#waterfall)
90 | * [queue](#queue)
91 | * [auto](#auto)
92 | * [iterator](#iterator)
93 | * [apply](#apply)
94 | * [nextTick](#nextTick)
95 |
96 | ### Utils
97 |
98 | * [memoize](#memoize)
99 | * [unmemoize](#unmemoize)
100 | * [log](#log)
101 | * [dir](#dir)
102 | * [noConflict](#noConflict)
103 |
104 |
105 | ## Collections
106 |
107 |
108 | ### forEach(arr, iterator, callback)
109 |
110 | Applies an iterator function to each item in an array, in parallel.
111 | The iterator is called with an item from the list and a callback for when it
112 | has finished. If the iterator passes an error to this callback, the main
113 | callback for the forEach function is immediately called with the error.
114 |
115 | Note, that since this function applies the iterator to each item in parallel
116 | there is no guarantee that the iterator functions will complete in order.
117 |
118 | __Arguments__
119 |
120 | * arr - An array to iterate over.
121 | * iterator(item, callback) - A function to apply to each item in the array.
122 | The iterator is passed a callback which must be called once it has completed.
123 | * callback(err) - A callback which is called after all the iterator functions
124 | have finished, or an error has occurred.
125 |
126 | __Example__
127 |
128 | // assuming openFiles is an array of file names and saveFile is a function
129 | // to save the modified contents of that file:
130 |
131 | async.forEach(openFiles, saveFile, function(err){
132 | // if any of the saves produced an error, err would equal that error
133 | });
134 |
135 | ---------------------------------------
136 |
137 |
138 | ### forEachSeries(arr, iterator, callback)
139 |
140 | The same as forEach only the iterator is applied to each item in the array in
141 | series. The next iterator is only called once the current one has completed
142 | processing. This means the iterator functions will complete in order.
143 |
144 |
145 | ---------------------------------------
146 |
147 |
148 | ### forEachLimit(arr, limit, iterator, callback)
149 |
150 | The same as forEach only the iterator is applied to batches of items in the
151 | array, in series. The next batch of iterators is only called once the current
152 | one has completed processing.
153 |
154 | __Arguments__
155 |
156 | * arr - An array to iterate over.
157 | * limit - How many items should be in each batch.
158 | * iterator(item, callback) - A function to apply to each item in the array.
159 | The iterator is passed a callback which must be called once it has completed.
160 | * callback(err) - A callback which is called after all the iterator functions
161 | have finished, or an error has occurred.
162 |
163 | __Example__
164 |
165 | // Assume documents is an array of JSON objects and requestApi is a
166 | // function that interacts with a rate-limited REST api.
167 |
168 | async.forEachLimit(documents, 20, requestApi, function(err){
169 | // if any of the saves produced an error, err would equal that error
170 | });
171 | ---------------------------------------
172 |
173 |
174 | ### map(arr, iterator, callback)
175 |
176 | Produces a new array of values by mapping each value in the given array through
177 | the iterator function. The iterator is called with an item from the array and a
178 | callback for when it has finished processing. The callback takes 2 arguments,
179 | an error and the transformed item from the array. If the iterator passes an
180 | error to this callback, the main callback for the map function is immediately
181 | called with the error.
182 |
183 | Note, that since this function applies the iterator to each item in parallel
184 | there is no guarantee that the iterator functions will complete in order, however
185 | the results array will be in the same order as the original array.
186 |
187 | __Arguments__
188 |
189 | * arr - An array to iterate over.
190 | * iterator(item, callback) - A function to apply to each item in the array.
191 | The iterator is passed a callback which must be called once it has completed
192 | with an error (which can be null) and a transformed item.
193 | * callback(err, results) - A callback which is called after all the iterator
194 | functions have finished, or an error has occurred. Results is an array of the
195 | transformed items from the original array.
196 |
197 | __Example__
198 |
199 | async.map(['file1','file2','file3'], fs.stat, function(err, results){
200 | // results is now an array of stats for each file
201 | });
202 |
203 | ---------------------------------------
204 |
205 |
206 | ### mapSeries(arr, iterator, callback)
207 |
208 | The same as map only the iterator is applied to each item in the array in
209 | series. The next iterator is only called once the current one has completed
210 | processing. The results array will be in the same order as the original.
211 |
212 |
213 | ---------------------------------------
214 |
215 |
216 | ### filter(arr, iterator, callback)
217 |
218 | __Alias:__ select
219 |
220 | Returns a new array of all the values which pass an async truth test.
221 | _The callback for each iterator call only accepts a single argument of true or
222 | false, it does not accept an error argument first!_ This is in-line with the
223 | way node libraries work with truth tests like path.exists. This operation is
224 | performed in parallel, but the results array will be in the same order as the
225 | original.
226 |
227 | __Arguments__
228 |
229 | * arr - An array to iterate over.
230 | * iterator(item, callback) - A truth test to apply to each item in the array.
231 | The iterator is passed a callback which must be called once it has completed.
232 | * callback(results) - A callback which is called after all the iterator
233 | functions have finished.
234 |
235 | __Example__
236 |
237 | async.filter(['file1','file2','file3'], path.exists, function(results){
238 | // results now equals an array of the existing files
239 | });
240 |
241 | ---------------------------------------
242 |
243 |
244 | ### filterSeries(arr, iterator, callback)
245 |
246 | __alias:__ selectSeries
247 |
248 | The same as filter only the iterator is applied to each item in the array in
249 | series. The next iterator is only called once the current one has completed
250 | processing. The results array will be in the same order as the original.
251 |
252 | ---------------------------------------
253 |
254 |
255 | ### reject(arr, iterator, callback)
256 |
257 | The opposite of filter. Removes values that pass an async truth test.
258 |
259 | ---------------------------------------
260 |
261 |
262 | ### rejectSeries(arr, iterator, callback)
263 |
264 | The same as filter, only the iterator is applied to each item in the array
265 | in series.
266 |
267 |
268 | ---------------------------------------
269 |
270 |
271 | ### reduce(arr, memo, iterator, callback)
272 |
273 | __aliases:__ inject, foldl
274 |
275 | Reduces a list of values into a single value using an async iterator to return
276 | each successive step. Memo is the initial state of the reduction. This
277 | function only operates in series. For performance reasons, it may make sense to
278 | split a call to this function into a parallel map, then use the normal
279 | Array.prototype.reduce on the results. This function is for situations where
280 | each step in the reduction needs to be async, if you can get the data before
281 | reducing it then its probably a good idea to do so.
282 |
283 | __Arguments__
284 |
285 | * arr - An array to iterate over.
286 | * memo - The initial state of the reduction.
287 | * iterator(memo, item, callback) - A function applied to each item in the
288 | array to produce the next step in the reduction. The iterator is passed a
289 | callback which accepts an optional error as its first argument, and the state
290 | of the reduction as the second. If an error is passed to the callback, the
291 | reduction is stopped and the main callback is immediately called with the
292 | error.
293 | * callback(err, result) - A callback which is called after all the iterator
294 | functions have finished. Result is the reduced value.
295 |
296 | __Example__
297 |
298 | async.reduce([1,2,3], 0, function(memo, item, callback){
299 | // pointless async:
300 | process.nextTick(function(){
301 | callback(null, memo + item)
302 | });
303 | }, function(err, result){
304 | // result is now equal to the last value of memo, which is 6
305 | });
306 |
307 | ---------------------------------------
308 |
309 |
310 | ### reduceRight(arr, memo, iterator, callback)
311 |
312 | __Alias:__ foldr
313 |
314 | Same as reduce, only operates on the items in the array in reverse order.
315 |
316 |
317 | ---------------------------------------
318 |
319 |
320 | ### detect(arr, iterator, callback)
321 |
322 | Returns the first value in a list that passes an async truth test. The
323 | iterator is applied in parallel, meaning the first iterator to return true will
324 | fire the detect callback with that result. That means the result might not be
325 | the first item in the original array (in terms of order) that passes the test.
326 |
327 | If order within the original array is important then look at detectSeries.
328 |
329 | __Arguments__
330 |
331 | * arr - An array to iterate over.
332 | * iterator(item, callback) - A truth test to apply to each item in the array.
333 | The iterator is passed a callback which must be called once it has completed.
334 | * callback(result) - A callback which is called as soon as any iterator returns
335 | true, or after all the iterator functions have finished. Result will be
336 | the first item in the array that passes the truth test (iterator) or the
337 | value undefined if none passed.
338 |
339 | __Example__
340 |
341 | async.detect(['file1','file2','file3'], path.exists, function(result){
342 | // result now equals the first file in the list that exists
343 | });
344 |
345 | ---------------------------------------
346 |
347 |
348 | ### detectSeries(arr, iterator, callback)
349 |
350 | The same as detect, only the iterator is applied to each item in the array
351 | in series. This means the result is always the first in the original array (in
352 | terms of array order) that passes the truth test.
353 |
354 |
355 | ---------------------------------------
356 |
357 |
358 | ### sortBy(arr, iterator, callback)
359 |
360 | Sorts a list by the results of running each value through an async iterator.
361 |
362 | __Arguments__
363 |
364 | * arr - An array to iterate over.
365 | * iterator(item, callback) - A function to apply to each item in the array.
366 | The iterator is passed a callback which must be called once it has completed
367 | with an error (which can be null) and a value to use as the sort criteria.
368 | * callback(err, results) - A callback which is called after all the iterator
369 | functions have finished, or an error has occurred. Results is the items from
370 | the original array sorted by the values returned by the iterator calls.
371 |
372 | __Example__
373 |
374 | async.sortBy(['file1','file2','file3'], function(file, callback){
375 | fs.stat(file, function(err, stats){
376 | callback(err, stats.mtime);
377 | });
378 | }, function(err, results){
379 | // results is now the original array of files sorted by
380 | // modified date
381 | });
382 |
383 |
384 | ---------------------------------------
385 |
386 |
387 | ### some(arr, iterator, callback)
388 |
389 | __Alias:__ any
390 |
391 | Returns true if at least one element in the array satisfies an async test.
392 | _The callback for each iterator call only accepts a single argument of true or
393 | false, it does not accept an error argument first!_ This is in-line with the
394 | way node libraries work with truth tests like path.exists. Once any iterator
395 | call returns true, the main callback is immediately called.
396 |
397 | __Arguments__
398 |
399 | * arr - An array to iterate over.
400 | * iterator(item, callback) - A truth test to apply to each item in the array.
401 | The iterator is passed a callback which must be called once it has completed.
402 | * callback(result) - A callback which is called as soon as any iterator returns
403 | true, or after all the iterator functions have finished. Result will be
404 | either true or false depending on the values of the async tests.
405 |
406 | __Example__
407 |
408 | async.some(['file1','file2','file3'], path.exists, function(result){
409 | // if result is true then at least one of the files exists
410 | });
411 |
412 | ---------------------------------------
413 |
414 |
415 | ### every(arr, iterator, callback)
416 |
417 | __Alias:__ all
418 |
419 | Returns true if every element in the array satisfies an async test.
420 | _The callback for each iterator call only accepts a single argument of true or
421 | false, it does not accept an error argument first!_ This is in-line with the
422 | way node libraries work with truth tests like path.exists.
423 |
424 | __Arguments__
425 |
426 | * arr - An array to iterate over.
427 | * iterator(item, callback) - A truth test to apply to each item in the array.
428 | The iterator is passed a callback which must be called once it has completed.
429 | * callback(result) - A callback which is called after all the iterator
430 | functions have finished. Result will be either true or false depending on
431 | the values of the async tests.
432 |
433 | __Example__
434 |
435 | async.every(['file1','file2','file3'], path.exists, function(result){
436 | // if result is true then every file exists
437 | });
438 |
439 | ---------------------------------------
440 |
441 |
442 | ### concat(arr, iterator, callback)
443 |
444 | Applies an iterator to each item in a list, concatenating the results. Returns the
445 | concatenated list. The iterators are called in parallel, and the results are
446 | concatenated as they return. There is no guarantee that the results array will
447 | be returned in the original order of the arguments passed to the iterator function.
448 |
449 | __Arguments__
450 |
451 | * arr - An array to iterate over
452 | * iterator(item, callback) - A function to apply to each item in the array.
453 | The iterator is passed a callback which must be called once it has completed
454 | with an error (which can be null) and an array of results.
455 | * callback(err, results) - A callback which is called after all the iterator
456 | functions have finished, or an error has occurred. Results is an array containing
457 | the concatenated results of the iterator function.
458 |
459 | __Example__
460 |
461 | async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
462 | // files is now a list of filenames that exist in the 3 directories
463 | });
464 |
465 | ---------------------------------------
466 |
467 |
468 | ### concatSeries(arr, iterator, callback)
469 |
470 | Same as async.concat, but executes in series instead of parallel.
471 |
472 |
473 | ## Control Flow
474 |
475 |
476 | ### series(tasks, [callback])
477 |
478 | Run an array of functions in series, each one running once the previous
479 | function has completed. If any functions in the series pass an error to its
480 | callback, no more functions are run and the callback for the series is
481 | immediately called with the value of the error. Once the tasks have completed,
482 | the results are passed to the final callback as an array.
483 |
484 | It is also possible to use an object instead of an array. Each property will be
485 | run as a function and the results will be passed to the final callback as an object
486 | instead of an array. This can be a more readable way of handling results from
487 | async.series.
488 |
489 |
490 | __Arguments__
491 |
492 | * tasks - An array or object containing functions to run, each function is passed
493 | a callback it must call on completion.
494 | * callback(err, results) - An optional callback to run once all the functions
495 | have completed. This function gets an array of all the arguments passed to
496 | the callbacks used in the array.
497 |
498 | __Example__
499 |
500 | async.series([
501 | function(callback){
502 | // do some stuff ...
503 | callback(null, 'one');
504 | },
505 | function(callback){
506 | // do some more stuff ...
507 | callback(null, 'two');
508 | },
509 | ],
510 | // optional callback
511 | function(err, results){
512 | // results is now equal to ['one', 'two']
513 | });
514 |
515 |
516 | // an example using an object instead of an array
517 | async.series({
518 | one: function(callback){
519 | setTimeout(function(){
520 | callback(null, 1);
521 | }, 200);
522 | },
523 | two: function(callback){
524 | setTimeout(function(){
525 | callback(null, 2);
526 | }, 100);
527 | },
528 | },
529 | function(err, results) {
530 | // results is now equal to: {one: 1, two: 2}
531 | });
532 |
533 |
534 | ---------------------------------------
535 |
536 |
537 | ### parallel(tasks, [callback])
538 |
539 | Run an array of functions in parallel, without waiting until the previous
540 | function has completed. If any of the functions pass an error to its
541 | callback, the main callback is immediately called with the value of the error.
542 | Once the tasks have completed, the results are passed to the final callback as an
543 | array.
544 |
545 | It is also possible to use an object instead of an array. Each property will be
546 | run as a function and the results will be passed to the final callback as an object
547 | instead of an array. This can be a more readable way of handling results from
548 | async.parallel.
549 |
550 |
551 | __Arguments__
552 |
553 | * tasks - An array or object containing functions to run, each function is passed a
554 | callback it must call on completion.
555 | * callback(err, results) - An optional callback to run once all the functions
556 | have completed. This function gets an array of all the arguments passed to
557 | the callbacks used in the array.
558 |
559 | __Example__
560 |
561 | async.parallel([
562 | function(callback){
563 | setTimeout(function(){
564 | callback(null, 'one');
565 | }, 200);
566 | },
567 | function(callback){
568 | setTimeout(function(){
569 | callback(null, 'two');
570 | }, 100);
571 | },
572 | ],
573 | // optional callback
574 | function(err, results){
575 | // in this case, the results array will equal ['two','one']
576 | // because the functions were run in parallel and the second
577 | // function had a shorter timeout before calling the callback.
578 | });
579 |
580 |
581 | // an example using an object instead of an array
582 | async.parallel({
583 | one: function(callback){
584 | setTimeout(function(){
585 | callback(null, 1);
586 | }, 200);
587 | },
588 | two: function(callback){
589 | setTimeout(function(){
590 | callback(null, 2);
591 | }, 100);
592 | },
593 | },
594 | function(err, results) {
595 | // results is now equals to: {one: 1, two: 2}
596 | });
597 |
598 |
599 | ---------------------------------------
600 |
601 |
602 | ### whilst(test, fn, callback)
603 |
604 | Repeatedly call fn, while test returns true. Calls the callback when stopped,
605 | or an error occurs.
606 |
607 | __Arguments__
608 |
609 | * test() - synchronous truth test to perform before each execution of fn.
610 | * fn(callback) - A function to call each time the test passes. The function is
611 | passed a callback which must be called once it has completed with an optional
612 | error as the first argument.
613 | * callback(err) - A callback which is called after the test fails and repeated
614 | execution of fn has stopped.
615 |
616 | __Example__
617 |
618 | var count = 0;
619 |
620 | async.whilst(
621 | function () { return count < 5; },
622 | function (callback) {
623 | count++;
624 | setTimeout(callback, 1000);
625 | },
626 | function (err) {
627 | // 5 seconds have passed
628 | }
629 | );
630 |
631 |
632 | ---------------------------------------
633 |
634 |
635 | ### until(test, fn, callback)
636 |
637 | Repeatedly call fn, until test returns true. Calls the callback when stopped,
638 | or an error occurs.
639 |
640 | The inverse of async.whilst.
641 |
642 |
643 | ---------------------------------------
644 |
645 |
646 | ### waterfall(tasks, [callback])
647 |
648 | Runs an array of functions in series, each passing their results to the next in
649 | the array. However, if any of the functions pass an error to the callback, the
650 | next function is not executed and the main callback is immediately called with
651 | the error.
652 |
653 | __Arguments__
654 |
655 | * tasks - An array of functions to run, each function is passed a callback it
656 | must call on completion.
657 | * callback(err, [results]) - An optional callback to run once all the functions
658 | have completed. This will be passed the results of the last task's callback.
659 |
660 |
661 |
662 | __Example__
663 |
664 | async.waterfall([
665 | function(callback){
666 | callback(null, 'one', 'two');
667 | },
668 | function(arg1, arg2, callback){
669 | callback(null, 'three');
670 | },
671 | function(arg1, callback){
672 | // arg1 now equals 'three'
673 | callback(null, 'done');
674 | }
675 | ], function (err, result) {
676 | // result now equals 'done'
677 | });
678 |
679 |
680 | ---------------------------------------
681 |
682 |
683 | ### queue(worker, concurrency)
684 |
685 | Creates a queue object with the specified concurrency. Tasks added to the
686 | queue will be processed in parallel (up to the concurrency limit). If all
687 | workers are in progress, the task is queued until one is available. Once
688 | a worker has completed a task, the task's callback is called.
689 |
690 | __Arguments__
691 |
692 | * worker(task, callback) - An asynchronous function for processing a queued
693 | task.
694 | * concurrency - An integer for determining how many worker functions should be
695 | run in parallel.
696 |
697 | __Queue objects__
698 |
699 | The queue object returned by this function has the following properties and
700 | methods:
701 |
702 | * length() - a function returning the number of items waiting to be processed.
703 | * concurrency - an integer for determining how many worker functions should be
704 | run in parallel. This property can be changed after a queue is created to
705 | alter the concurrency on-the-fly.
706 | * push(task, [callback]) - add a new task to the queue, the callback is called
707 | once the worker has finished processing the task.
708 | instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.
709 | * saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued
710 | * empty - a callback that is called when the last item from the queue is given to a worker
711 | * drain - a callback that is called when the last item from the queue has returned from the worker
712 |
713 | __Example__
714 |
715 | // create a queue object with concurrency 2
716 |
717 | var q = async.queue(function (task, callback) {
718 | console.log('hello ' + task.name);
719 | callback();
720 | }, 2);
721 |
722 |
723 | // assign a callback
724 | q.drain = function() {
725 | console.log('all items have been processed');
726 | }
727 |
728 | // add some items to the queue
729 |
730 | q.push({name: 'foo'}, function (err) {
731 | console.log('finished processing foo');
732 | });
733 | q.push({name: 'bar'}, function (err) {
734 | console.log('finished processing bar');
735 | });
736 |
737 | // add some items to the queue (batch-wise)
738 |
739 | q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {
740 | console.log('finished processing bar');
741 | });
742 |
743 |
744 | ---------------------------------------
745 |
746 |
747 | ### auto(tasks, [callback])
748 |
749 | Determines the best order for running functions based on their requirements.
750 | Each function can optionally depend on other functions being completed first,
751 | and each function is run as soon as its requirements are satisfied. If any of
752 | the functions pass an error to their callback, that function will not complete
753 | (so any other functions depending on it will not run) and the main callback
754 | will be called immediately with the error. Functions also receive an object
755 | containing the results of functions which have completed so far.
756 |
757 | __Arguments__
758 |
759 | * tasks - An object literal containing named functions or an array of
760 | requirements, with the function itself the last item in the array. The key
761 | used for each function or array is used when specifying requirements. The
762 | syntax is easier to understand by looking at the example.
763 | * callback(err, results) - An optional callback which is called when all the
764 | tasks have been completed. The callback will receive an error as an argument
765 | if any tasks pass an error to their callback. If all tasks complete
766 | successfully, it will receive an object containing their results.
767 |
768 | __Example__
769 |
770 | async.auto({
771 | get_data: function(callback){
772 | // async code to get some data
773 | },
774 | make_folder: function(callback){
775 | // async code to create a directory to store a file in
776 | // this is run at the same time as getting the data
777 | },
778 | write_file: ['get_data', 'make_folder', function(callback){
779 | // once there is some data and the directory exists,
780 | // write the data to a file in the directory
781 | callback(null, filename);
782 | }],
783 | email_link: ['write_file', function(callback, results){
784 | // once the file is written let's email a link to it...
785 | // results.write_file contains the filename returned by write_file.
786 | }]
787 | });
788 |
789 | This is a fairly trivial example, but to do this using the basic parallel and
790 | series functions would look like this:
791 |
792 | async.parallel([
793 | function(callback){
794 | // async code to get some data
795 | },
796 | function(callback){
797 | // async code to create a directory to store a file in
798 | // this is run at the same time as getting the data
799 | }
800 | ],
801 | function(results){
802 | async.series([
803 | function(callback){
804 | // once there is some data and the directory exists,
805 | // write the data to a file in the directory
806 | },
807 | email_link: function(callback){
808 | // once the file is written let's email a link to it...
809 | }
810 | ]);
811 | });
812 |
813 | For a complicated series of async tasks using the auto function makes adding
814 | new tasks much easier and makes the code more readable.
815 |
816 |
817 | ---------------------------------------
818 |
819 |
820 | ### iterator(tasks)
821 |
822 | Creates an iterator function which calls the next function in the array,
823 | returning a continuation to call the next one after that. Its also possible to
824 | 'peek' the next iterator by doing iterator.next().
825 |
826 | This function is used internally by the async module but can be useful when
827 | you want to manually control the flow of functions in series.
828 |
829 | __Arguments__
830 |
831 | * tasks - An array of functions to run, each function is passed a callback it
832 | must call on completion.
833 |
834 | __Example__
835 |
836 | var iterator = async.iterator([
837 | function(){ sys.p('one'); },
838 | function(){ sys.p('two'); },
839 | function(){ sys.p('three'); }
840 | ]);
841 |
842 | node> var iterator2 = iterator();
843 | 'one'
844 | node> var iterator3 = iterator2();
845 | 'two'
846 | node> iterator3();
847 | 'three'
848 | node> var nextfn = iterator2.next();
849 | node> nextfn();
850 | 'three'
851 |
852 |
853 | ---------------------------------------
854 |
855 |
856 | ### apply(function, arguments..)
857 |
858 | Creates a continuation function with some arguments already applied, a useful
859 | shorthand when combined with other control flow functions. Any arguments
860 | passed to the returned function are added to the arguments originally passed
861 | to apply.
862 |
863 | __Arguments__
864 |
865 | * function - The function you want to eventually apply all arguments to.
866 | * arguments... - Any number of arguments to automatically apply when the
867 | continuation is called.
868 |
869 | __Example__
870 |
871 | // using apply
872 |
873 | async.parallel([
874 | async.apply(fs.writeFile, 'testfile1', 'test1'),
875 | async.apply(fs.writeFile, 'testfile2', 'test2'),
876 | ]);
877 |
878 |
879 | // the same process without using apply
880 |
881 | async.parallel([
882 | function(callback){
883 | fs.writeFile('testfile1', 'test1', callback);
884 | },
885 | function(callback){
886 | fs.writeFile('testfile2', 'test2', callback);
887 | },
888 | ]);
889 |
890 | It's possible to pass any number of additional arguments when calling the
891 | continuation:
892 |
893 | node> var fn = async.apply(sys.puts, 'one');
894 | node> fn('two', 'three');
895 | one
896 | two
897 | three
898 |
899 | ---------------------------------------
900 |
901 |
902 | ### nextTick(callback)
903 |
904 | Calls the callback on a later loop around the event loop. In node.js this just
905 | calls process.nextTick, in the browser it falls back to setTimeout(callback, 0),
906 | which means other higher priority events may precede the execution of the callback.
907 |
908 | This is used internally for browser-compatibility purposes.
909 |
910 | __Arguments__
911 |
912 | * callback - The function to call on a later loop around the event loop.
913 |
914 | __Example__
915 |
916 | var call_order = [];
917 | async.nextTick(function(){
918 | call_order.push('two');
919 | // call_order now equals ['one','two]
920 | });
921 | call_order.push('one')
922 |
923 |
924 | ## Utils
925 |
926 |
927 | ### memoize(fn, [hasher])
928 |
929 | Caches the results of an async function. When creating a hash to store function
930 | results against, the callback is omitted from the hash and an optional hash
931 | function can be used.
932 |
933 | __Arguments__
934 |
935 | * fn - the function you to proxy and cache results from.
936 | * hasher - an optional function for generating a custom hash for storing
937 | results, it has all the arguments applied to it apart from the callback, and
938 | must be synchronous.
939 |
940 | __Example__
941 |
942 | var slow_fn = function (name, callback) {
943 | // do something
944 | callback(null, result);
945 | };
946 | var fn = async.memoize(slow_fn);
947 |
948 | // fn can now be used as if it were slow_fn
949 | fn('some name', function () {
950 | // callback
951 | });
952 |
953 |
954 | ### unmemoize(fn)
955 |
956 | Undoes a memoized function, reverting it to the original, unmemoized
957 | form. Comes handy in tests.
958 |
959 | __Arguments__
960 |
961 | * fn - the memoized function
962 |
963 |
964 | ### log(function, arguments)
965 |
966 | Logs the result of an async function to the console. Only works in node.js or
967 | in browsers that support console.log and console.error (such as FF and Chrome).
968 | If multiple arguments are returned from the async function, console.log is
969 | called on each argument in order.
970 |
971 | __Arguments__
972 |
973 | * function - The function you want to eventually apply all arguments to.
974 | * arguments... - Any number of arguments to apply to the function.
975 |
976 | __Example__
977 |
978 | var hello = function(name, callback){
979 | setTimeout(function(){
980 | callback(null, 'hello ' + name);
981 | }, 1000);
982 | };
983 |
984 | node> async.log(hello, 'world');
985 | 'hello world'
986 |
987 |
988 | ---------------------------------------
989 |
990 |
991 | ### dir(function, arguments)
992 |
993 | Logs the result of an async function to the console using console.dir to
994 | display the properties of the resulting object. Only works in node.js or
995 | in browsers that support console.dir and console.error (such as FF and Chrome).
996 | If multiple arguments are returned from the async function, console.dir is
997 | called on each argument in order.
998 |
999 | __Arguments__
1000 |
1001 | * function - The function you want to eventually apply all arguments to.
1002 | * arguments... - Any number of arguments to apply to the function.
1003 |
1004 | __Example__
1005 |
1006 | var hello = function(name, callback){
1007 | setTimeout(function(){
1008 | callback(null, {hello: name});
1009 | }, 1000);
1010 | };
1011 |
1012 | node> async.dir(hello, 'world');
1013 | {hello: 'world'}
1014 |
1015 |
1016 | ---------------------------------------
1017 |
1018 |
1019 | ### noConflict()
1020 |
1021 | Changes the value of async back to its original value, returning a reference to the
1022 | async object.
1023 |
--------------------------------------------------------------------------------
/test/test-async.js:
--------------------------------------------------------------------------------
1 | var async = require('../lib/async');
2 |
3 | if (!Function.prototype.bind) {
4 | Function.prototype.bind = function (thisArg) {
5 | var args = Array.prototype.slice.call(arguments, 1);
6 | var self = this;
7 | return function () {
8 | self.apply(thisArg, args.concat(Array.prototype.slice.call(arguments)));
9 | }
10 | };
11 | }
12 |
13 | function forEachIterator(args, x, callback) {
14 | setTimeout(function(){
15 | args.push(x);
16 | callback();
17 | }, x*25);
18 | }
19 |
20 | function mapIterator(call_order, x, callback) {
21 | setTimeout(function(){
22 | call_order.push(x);
23 | callback(null, x*2);
24 | }, x*25);
25 | }
26 |
27 | function filterIterator(x, callback) {
28 | setTimeout(function(){
29 | callback(x % 2);
30 | }, x*25);
31 | }
32 |
33 | function detectIterator(call_order, x, callback) {
34 | setTimeout(function(){
35 | call_order.push(x);
36 | callback(x == 2);
37 | }, x*25);
38 | }
39 |
40 | function forEachNoCallbackIterator(test, x, callback) {
41 | test.equal(x, 1);
42 | callback();
43 | test.done();
44 | }
45 |
46 | function getFunctionsObject(call_order) {
47 | return {
48 | one: function(callback){
49 | setTimeout(function(){
50 | call_order.push(1);
51 | callback(null, 1);
52 | }, 25);
53 | },
54 | two: function(callback){
55 | setTimeout(function(){
56 | call_order.push(2);
57 | callback(null, 2);
58 | }, 50);
59 | },
60 | three: function(callback){
61 | setTimeout(function(){
62 | call_order.push(3);
63 | callback(null, 3,3);
64 | }, 15);
65 | }
66 | };
67 | }
68 |
69 | exports['auto'] = function(test){
70 | var callOrder = [];
71 | var testdata = [{test: 'test'}];
72 | async.auto({
73 | task1: ['task2', function(callback){
74 | setTimeout(function(){
75 | callOrder.push('task1');
76 | callback();
77 | }, 25);
78 | }],
79 | task2: function(callback){
80 | setTimeout(function(){
81 | callOrder.push('task2');
82 | callback();
83 | }, 50);
84 | },
85 | task3: ['task2', function(callback){
86 | callOrder.push('task3');
87 | callback();
88 | }],
89 | task4: ['task1', 'task2', function(callback){
90 | callOrder.push('task4');
91 | callback();
92 | }]
93 | },
94 | function(err){
95 | test.same(callOrder, ['task2','task3','task1','task4']);
96 | test.done();
97 | });
98 | };
99 |
100 | exports['auto results'] = function(test){
101 | var callOrder = [];
102 | async.auto({
103 | task1: ['task2', function(callback, results){
104 | test.same(results.task2, 'task2');
105 | setTimeout(function(){
106 | callOrder.push('task1');
107 | callback(null, 'task1a', 'task1b');
108 | }, 25);
109 | }],
110 | task2: function(callback){
111 | setTimeout(function(){
112 | callOrder.push('task2');
113 | callback(null, 'task2');
114 | }, 50);
115 | },
116 | task3: ['task2', function(callback, results){
117 | test.same(results.task2, 'task2');
118 | callOrder.push('task3');
119 | callback(null);
120 | }],
121 | task4: ['task1', 'task2', function(callback, results){
122 | test.same(results.task1, ['task1a','task1b']);
123 | test.same(results.task2, 'task2');
124 | callOrder.push('task4');
125 | callback(null, 'task4');
126 | }]
127 | },
128 | function(err, results){
129 | test.same(callOrder, ['task2','task3','task1','task4']);
130 | test.same(results, {task1: ['task1a','task1b'], task2: 'task2', task3: undefined, task4: 'task4'});
131 | test.done();
132 | });
133 | };
134 |
135 |
136 | exports['auto empty object'] = function(test){
137 | async.auto({}, function(err){
138 | test.done();
139 | });
140 | };
141 |
142 | exports['auto error'] = function(test){
143 | test.expect(1);
144 | async.auto({
145 | task1: function(callback){
146 | callback('testerror');
147 | },
148 | task2: ['task1', function(callback){
149 | test.ok(false, 'task2 should not be called');
150 | callback();
151 | }],
152 | task3: function(callback){
153 | callback('testerror2');
154 | }
155 | },
156 | function(err){
157 | test.equals(err, 'testerror');
158 | });
159 | setTimeout(test.done, 100);
160 | };
161 |
162 | exports['auto no callback'] = function(test){
163 | async.auto({
164 | task1: function(callback){callback();},
165 | task2: ['task1', function(callback){callback(); test.done();}]
166 | });
167 | };
168 |
169 | // Issue 24 on github: https://github.com/caolan/async/issues#issue/24
170 | // Issue 76 on github: https://github.com/caolan/async/issues#issue/76
171 | exports['auto removeListener has side effect on loop iterator'] = function(test) {
172 | async.auto({
173 | task1: ['task3', function(callback) { test.done() }],
174 | task2: ['task3', function(callback) { /* by design: DON'T call callback */ }],
175 | task3: function(callback) { callback(); }
176 | });
177 | };
178 |
179 | exports['waterfall'] = function(test){
180 | test.expect(6);
181 | var call_order = [];
182 | async.waterfall([
183 | function(callback){
184 | call_order.push('fn1');
185 | setTimeout(function(){callback(null, 'one', 'two');}, 0);
186 | },
187 | function(arg1, arg2, callback){
188 | call_order.push('fn2');
189 | test.equals(arg1, 'one');
190 | test.equals(arg2, 'two');
191 | setTimeout(function(){callback(null, arg1, arg2, 'three');}, 25);
192 | },
193 | function(arg1, arg2, arg3, callback){
194 | call_order.push('fn3');
195 | test.equals(arg1, 'one');
196 | test.equals(arg2, 'two');
197 | test.equals(arg3, 'three');
198 | callback(null, 'four');
199 | },
200 | function(arg4, callback){
201 | call_order.push('fn4');
202 | test.same(call_order, ['fn1','fn2','fn3','fn4']);
203 | callback(null, 'test');
204 | }
205 | ], function(err){
206 | test.done();
207 | });
208 | };
209 |
210 | exports['waterfall empty array'] = function(test){
211 | async.waterfall([], function(err){
212 | test.done();
213 | });
214 | };
215 |
216 | exports['waterfall no callback'] = function(test){
217 | async.waterfall([
218 | function(callback){callback();},
219 | function(callback){callback(); test.done();}
220 | ]);
221 | };
222 |
223 | exports['waterfall async'] = function(test){
224 | var call_order = [];
225 | async.waterfall([
226 | function(callback){
227 | call_order.push(1);
228 | callback();
229 | call_order.push(2);
230 | },
231 | function(callback){
232 | call_order.push(3);
233 | callback();
234 | },
235 | function(){
236 | test.same(call_order, [1,2,3]);
237 | test.done();
238 | }
239 | ]);
240 | };
241 |
242 | exports['waterfall error'] = function(test){
243 | test.expect(1);
244 | async.waterfall([
245 | function(callback){
246 | callback('error');
247 | },
248 | function(callback){
249 | test.ok(false, 'next function should not be called');
250 | callback();
251 | }
252 | ], function(err){
253 | test.equals(err, 'error');
254 | });
255 | setTimeout(test.done, 50);
256 | };
257 |
258 | exports['waterfall multiple callback calls'] = function(test){
259 | var call_order = [];
260 | var arr = [
261 | function(callback){
262 | call_order.push(1);
263 | // call the callback twice. this should call function 2 twice
264 | callback(null, 'one', 'two');
265 | callback(null, 'one', 'two');
266 | },
267 | function(arg1, arg2, callback){
268 | call_order.push(2);
269 | callback(null, arg1, arg2, 'three');
270 | },
271 | function(arg1, arg2, arg3, callback){
272 | call_order.push(3);
273 | callback(null, 'four');
274 | },
275 | function(arg4){
276 | call_order.push(4);
277 | arr[3] = function(){
278 | call_order.push(4);
279 | test.same(call_order, [1,2,2,3,3,4,4]);
280 | test.done();
281 | };
282 | }
283 | ];
284 | async.waterfall(arr);
285 | };
286 |
287 |
288 | exports['parallel'] = function(test){
289 | var call_order = [];
290 | async.parallel([
291 | function(callback){
292 | setTimeout(function(){
293 | call_order.push(1);
294 | callback(null, 1);
295 | }, 50);
296 | },
297 | function(callback){
298 | setTimeout(function(){
299 | call_order.push(2);
300 | callback(null, 2);
301 | }, 100);
302 | },
303 | function(callback){
304 | setTimeout(function(){
305 | call_order.push(3);
306 | callback(null, 3,3);
307 | }, 25);
308 | }
309 | ],
310 | function(err, results){
311 | test.equals(err, null);
312 | test.same(call_order, [3,1,2]);
313 | test.same(results, [1,2,[3,3]]);
314 | test.done();
315 | });
316 | };
317 |
318 | exports['parallel empty array'] = function(test){
319 | async.parallel([], function(err, results){
320 | test.equals(err, null);
321 | test.same(results, []);
322 | test.done();
323 | });
324 | };
325 |
326 | exports['parallel error'] = function(test){
327 | async.parallel([
328 | function(callback){
329 | callback('error', 1);
330 | },
331 | function(callback){
332 | callback('error2', 2);
333 | }
334 | ],
335 | function(err, results){
336 | test.equals(err, 'error');
337 | });
338 | setTimeout(test.done, 100);
339 | };
340 |
341 | exports['parallel no callback'] = function(test){
342 | async.parallel([
343 | function(callback){callback();},
344 | function(callback){callback(); test.done();},
345 | ]);
346 | };
347 |
348 | exports['parallel object'] = function(test){
349 | var call_order = [];
350 | async.parallel(getFunctionsObject(call_order), function(err, results){
351 | test.equals(err, null);
352 | test.same(call_order, [3,1,2]);
353 | test.same(results, {
354 | one: 1,
355 | two: 2,
356 | three: [3,3]
357 | });
358 | test.done();
359 | });
360 | };
361 |
362 | exports['series'] = function(test){
363 | var call_order = [];
364 | async.series([
365 | function(callback){
366 | setTimeout(function(){
367 | call_order.push(1);
368 | callback(null, 1);
369 | }, 25);
370 | },
371 | function(callback){
372 | setTimeout(function(){
373 | call_order.push(2);
374 | callback(null, 2);
375 | }, 50);
376 | },
377 | function(callback){
378 | setTimeout(function(){
379 | call_order.push(3);
380 | callback(null, 3,3);
381 | }, 15);
382 | }
383 | ],
384 | function(err, results){
385 | test.equals(err, null);
386 | test.same(results, [1,2,[3,3]]);
387 | test.same(call_order, [1,2,3]);
388 | test.done();
389 | });
390 | };
391 |
392 | exports['series empty array'] = function(test){
393 | async.series([], function(err, results){
394 | test.equals(err, null);
395 | test.same(results, []);
396 | test.done();
397 | });
398 | };
399 |
400 | exports['series error'] = function(test){
401 | test.expect(1);
402 | async.series([
403 | function(callback){
404 | callback('error', 1);
405 | },
406 | function(callback){
407 | test.ok(false, 'should not be called');
408 | callback('error2', 2);
409 | }
410 | ],
411 | function(err, results){
412 | test.equals(err, 'error');
413 | });
414 | setTimeout(test.done, 100);
415 | };
416 |
417 | exports['series no callback'] = function(test){
418 | async.series([
419 | function(callback){callback();},
420 | function(callback){callback(); test.done();},
421 | ]);
422 | };
423 |
424 | exports['series object'] = function(test){
425 | var call_order = [];
426 | async.series(getFunctionsObject(call_order), function(err, results){
427 | test.equals(err, null);
428 | test.same(results, {
429 | one: 1,
430 | two: 2,
431 | three: [3,3]
432 | });
433 | test.same(call_order, [1,2,3]);
434 | test.done();
435 | });
436 | };
437 |
438 | exports['iterator'] = function(test){
439 | var call_order = [];
440 | var iterator = async.iterator([
441 | function(){call_order.push(1);},
442 | function(arg1){
443 | test.equals(arg1, 'arg1');
444 | call_order.push(2);
445 | },
446 | function(arg1, arg2){
447 | test.equals(arg1, 'arg1');
448 | test.equals(arg2, 'arg2');
449 | call_order.push(3);
450 | }
451 | ]);
452 | iterator();
453 | test.same(call_order, [1]);
454 | var iterator2 = iterator();
455 | test.same(call_order, [1,1]);
456 | var iterator3 = iterator2('arg1');
457 | test.same(call_order, [1,1,2]);
458 | var iterator4 = iterator3('arg1', 'arg2');
459 | test.same(call_order, [1,1,2,3]);
460 | test.equals(iterator4, undefined);
461 | test.done();
462 | };
463 |
464 | exports['iterator empty array'] = function(test){
465 | var iterator = async.iterator([]);
466 | test.equals(iterator(), undefined);
467 | test.equals(iterator.next(), undefined);
468 | test.done();
469 | };
470 |
471 | exports['iterator.next'] = function(test){
472 | var call_order = [];
473 | var iterator = async.iterator([
474 | function(){call_order.push(1);},
475 | function(arg1){
476 | test.equals(arg1, 'arg1');
477 | call_order.push(2);
478 | },
479 | function(arg1, arg2){
480 | test.equals(arg1, 'arg1');
481 | test.equals(arg2, 'arg2');
482 | call_order.push(3);
483 | }
484 | ]);
485 | var fn = iterator.next();
486 | var iterator2 = fn('arg1');
487 | test.same(call_order, [2]);
488 | iterator2('arg1','arg2');
489 | test.same(call_order, [2,3]);
490 | test.equals(iterator2.next(), undefined);
491 | test.done();
492 | };
493 |
494 | exports['forEach'] = function(test){
495 | var args = [];
496 | async.forEach([1,3,2], forEachIterator.bind(this, args), function(err){
497 | test.same(args, [1,2,3]);
498 | test.done();
499 | });
500 | };
501 |
502 | exports['forEach empty array'] = function(test){
503 | test.expect(1);
504 | async.forEach([], function(x, callback){
505 | test.ok(false, 'iterator should not be called');
506 | callback();
507 | }, function(err){
508 | test.ok(true, 'should call callback');
509 | });
510 | setTimeout(test.done, 25);
511 | };
512 |
513 | exports['forEach error'] = function(test){
514 | test.expect(1);
515 | async.forEach([1,2,3], function(x, callback){
516 | callback('error');
517 | }, function(err){
518 | test.equals(err, 'error');
519 | });
520 | setTimeout(test.done, 50);
521 | };
522 |
523 | exports['forEach no callback'] = function(test){
524 | async.forEach([1], forEachNoCallbackIterator.bind(this, test));
525 | };
526 |
527 | exports['forEachSeries'] = function(test){
528 | var args = [];
529 | async.forEachSeries([1,3,2], forEachIterator.bind(this, args), function(err){
530 | test.same(args, [1,3,2]);
531 | test.done();
532 | });
533 | };
534 |
535 | exports['forEachSeries empty array'] = function(test){
536 | test.expect(1);
537 | async.forEachSeries([], function(x, callback){
538 | test.ok(false, 'iterator should not be called');
539 | callback();
540 | }, function(err){
541 | test.ok(true, 'should call callback');
542 | });
543 | setTimeout(test.done, 25);
544 | };
545 |
546 | exports['forEachSeries error'] = function(test){
547 | test.expect(2);
548 | var call_order = [];
549 | async.forEachSeries([1,2,3], function(x, callback){
550 | call_order.push(x);
551 | callback('error');
552 | }, function(err){
553 | test.same(call_order, [1]);
554 | test.equals(err, 'error');
555 | });
556 | setTimeout(test.done, 50);
557 | };
558 |
559 | exports['forEachSeries no callback'] = function(test){
560 | async.forEachSeries([1], forEachNoCallbackIterator.bind(this, test));
561 | };
562 |
563 | exports['forEachLimit'] = function(test){
564 | var args = [];
565 | var arr = [0,1,2,3,4,5,6,7,8,9];
566 | async.forEachLimit(arr, 2, function(x,callback){
567 | setTimeout(function(){
568 | args.push(x);
569 | callback();
570 | }, x*5);
571 | }, function(err){
572 | test.same(args, arr);
573 | test.done();
574 | });
575 | };
576 |
577 | exports['forEachLimit empty array'] = function(test){
578 | test.expect(1);
579 | async.forEachLimit([], 2, function(x, callback){
580 | test.ok(false, 'iterator should not be called');
581 | callback();
582 | }, function(err){
583 | test.ok(true, 'should call callback');
584 | });
585 | setTimeout(test.done, 25);
586 | };
587 |
588 | exports['forEachLimit limit exceeds size'] = function(test){
589 | var args = [];
590 | var arr = [0,1,2,3,4,5,6,7,8,9];
591 | async.forEachLimit(arr, 20, forEachIterator.bind(this, args), function(err){
592 | test.same(args, arr);
593 | test.done();
594 | });
595 | };
596 |
597 | exports['forEachLimit limit equal size'] = function(test){
598 | var args = [];
599 | var arr = [0,1,2,3,4,5,6,7,8,9];
600 | async.forEachLimit(arr, 10, forEachIterator.bind(this, args), function(err){
601 | test.same(args, arr);
602 | test.done();
603 | });
604 | };
605 |
606 | exports['forEachLimit zero limit'] = function(test){
607 | test.expect(1);
608 | async.forEachLimit([0,1,2,3,4,5], 0, function(x, callback){
609 | test.ok(false, 'iterator should not be called');
610 | callback();
611 | }, function(err){
612 | test.ok(true, 'should call callback');
613 | });
614 | setTimeout(test.done, 25);
615 | };
616 |
617 | exports['forEachLimit error'] = function(test){
618 | test.expect(2);
619 | var arr = [0,1,2,3,4,5,6,7,8,9];
620 | var call_order = [];
621 |
622 | async.forEachLimit(arr, 3, function(x, callback){
623 | call_order.push(x);
624 | if (x === 2) {
625 | callback('error');
626 | }
627 | }, function(err){
628 | test.same(call_order, [0,1,2]);
629 | test.equals(err, 'error');
630 | });
631 | setTimeout(test.done, 25);
632 | };
633 |
634 | exports['forEachLimit no callback'] = function(test){
635 | async.forEachLimit([1], 1, forEachNoCallbackIterator.bind(this, test));
636 | };
637 |
638 | exports['map'] = function(test){
639 | var call_order = [];
640 | async.map([1,3,2], mapIterator.bind(this, call_order), function(err, results){
641 | test.same(call_order, [1,2,3]);
642 | test.same(results, [2,6,4]);
643 | test.done();
644 | });
645 | };
646 |
647 | exports['map original untouched'] = function(test){
648 | var a = [1,2,3];
649 | async.map(a, function(x, callback){
650 | callback(null, x*2);
651 | }, function(err, results){
652 | test.same(results, [2,4,6]);
653 | test.same(a, [1,2,3]);
654 | test.done();
655 | });
656 | };
657 |
658 | exports['map error'] = function(test){
659 | test.expect(1);
660 | async.map([1,2,3], function(x, callback){
661 | callback('error');
662 | }, function(err, results){
663 | test.equals(err, 'error');
664 | });
665 | setTimeout(test.done, 50);
666 | };
667 |
668 | exports['mapSeries'] = function(test){
669 | var call_order = [];
670 | async.mapSeries([1,3,2], mapIterator.bind(this, call_order), function(err, results){
671 | test.same(call_order, [1,3,2]);
672 | test.same(results, [2,6,4]);
673 | test.done();
674 | });
675 | };
676 |
677 | exports['mapSeries error'] = function(test){
678 | test.expect(1);
679 | async.mapSeries([1,2,3], function(x, callback){
680 | callback('error');
681 | }, function(err, results){
682 | test.equals(err, 'error');
683 | });
684 | setTimeout(test.done, 50);
685 | };
686 |
687 | exports['reduce'] = function(test){
688 | var call_order = [];
689 | async.reduce([1,2,3], 0, function(a, x, callback){
690 | call_order.push(x);
691 | callback(null, a + x);
692 | }, function(err, result){
693 | test.equals(result, 6);
694 | test.same(call_order, [1,2,3]);
695 | test.done();
696 | });
697 | };
698 |
699 | exports['reduce async with non-reference memo'] = function(test){
700 | async.reduce([1,3,2], 0, function(a, x, callback){
701 | setTimeout(function(){callback(null, a + x)}, Math.random()*100);
702 | }, function(err, result){
703 | test.equals(result, 6);
704 | test.done();
705 | });
706 | };
707 |
708 | exports['reduce error'] = function(test){
709 | test.expect(1);
710 | async.reduce([1,2,3], 0, function(a, x, callback){
711 | callback('error');
712 | }, function(err, result){
713 | test.equals(err, 'error');
714 | });
715 | setTimeout(test.done, 50);
716 | };
717 |
718 | exports['inject alias'] = function(test){
719 | test.equals(async.inject, async.reduce);
720 | test.done();
721 | };
722 |
723 | exports['foldl alias'] = function(test){
724 | test.equals(async.foldl, async.reduce);
725 | test.done();
726 | };
727 |
728 | exports['reduceRight'] = function(test){
729 | var call_order = [];
730 | var a = [1,2,3];
731 | async.reduceRight(a, 0, function(a, x, callback){
732 | call_order.push(x);
733 | callback(null, a + x);
734 | }, function(err, result){
735 | test.equals(result, 6);
736 | test.same(call_order, [3,2,1]);
737 | test.same(a, [1,2,3]);
738 | test.done();
739 | });
740 | };
741 |
742 | exports['foldr alias'] = function(test){
743 | test.equals(async.foldr, async.reduceRight);
744 | test.done();
745 | };
746 |
747 | exports['filter'] = function(test){
748 | async.filter([3,1,2], filterIterator, function(results){
749 | test.same(results, [3,1]);
750 | test.done();
751 | });
752 | };
753 |
754 | exports['filter original untouched'] = function(test){
755 | var a = [3,1,2];
756 | async.filter(a, function(x, callback){
757 | callback(x % 2);
758 | }, function(results){
759 | test.same(results, [3,1]);
760 | test.same(a, [3,1,2]);
761 | test.done();
762 | });
763 | };
764 |
765 | exports['filterSeries'] = function(test){
766 | async.filterSeries([3,1,2], filterIterator, function(results){
767 | test.same(results, [3,1]);
768 | test.done();
769 | });
770 | };
771 |
772 | exports['select alias'] = function(test){
773 | test.equals(async.select, async.filter);
774 | test.done();
775 | };
776 |
777 | exports['selectSeries alias'] = function(test){
778 | test.equals(async.selectSeries, async.filterSeries);
779 | test.done();
780 | };
781 |
782 | exports['reject'] = function(test){
783 | async.reject([3,1,2], filterIterator, function(results){
784 | test.same(results, [2]);
785 | test.done();
786 | });
787 | };
788 |
789 | exports['reject original untouched'] = function(test){
790 | var a = [3,1,2];
791 | async.reject(a, function(x, callback){
792 | callback(x % 2);
793 | }, function(results){
794 | test.same(results, [2]);
795 | test.same(a, [3,1,2]);
796 | test.done();
797 | });
798 | };
799 |
800 | exports['rejectSeries'] = function(test){
801 | async.rejectSeries([3,1,2], filterIterator, function(results){
802 | test.same(results, [2]);
803 | test.done();
804 | });
805 | };
806 |
807 | exports['some true'] = function(test){
808 | async.some([3,1,2], function(x, callback){
809 | setTimeout(function(){callback(x === 1);}, 0);
810 | }, function(result){
811 | test.equals(result, true);
812 | test.done();
813 | });
814 | };
815 |
816 | exports['some false'] = function(test){
817 | async.some([3,1,2], function(x, callback){
818 | setTimeout(function(){callback(x === 10);}, 0);
819 | }, function(result){
820 | test.equals(result, false);
821 | test.done();
822 | });
823 | };
824 |
825 | exports['some early return'] = function(test){
826 | var call_order = [];
827 | async.some([1,2,3], function(x, callback){
828 | setTimeout(function(){
829 | call_order.push(x);
830 | callback(x === 1);
831 | }, x*25);
832 | }, function(result){
833 | call_order.push('callback');
834 | });
835 | setTimeout(function(){
836 | test.same(call_order, [1,'callback',2,3]);
837 | test.done();
838 | }, 100);
839 | };
840 |
841 | exports['any alias'] = function(test){
842 | test.equals(async.any, async.some);
843 | test.done();
844 | };
845 |
846 | exports['every true'] = function(test){
847 | async.every([1,2,3], function(x, callback){
848 | setTimeout(function(){callback(true);}, 0);
849 | }, function(result){
850 | test.equals(result, true);
851 | test.done();
852 | });
853 | };
854 |
855 | exports['every false'] = function(test){
856 | async.every([1,2,3], function(x, callback){
857 | setTimeout(function(){callback(x % 2);}, 0);
858 | }, function(result){
859 | test.equals(result, false);
860 | test.done();
861 | });
862 | };
863 |
864 | exports['every early return'] = function(test){
865 | var call_order = [];
866 | async.every([1,2,3], function(x, callback){
867 | setTimeout(function(){
868 | call_order.push(x);
869 | callback(x === 1);
870 | }, x*25);
871 | }, function(result){
872 | call_order.push('callback');
873 | });
874 | setTimeout(function(){
875 | test.same(call_order, [1,2,'callback',3]);
876 | test.done();
877 | }, 100);
878 | };
879 |
880 | exports['all alias'] = function(test){
881 | test.equals(async.all, async.every);
882 | test.done();
883 | };
884 |
885 | exports['detect'] = function(test){
886 | var call_order = [];
887 | async.detect([3,2,1], detectIterator.bind(this, call_order), function(result){
888 | call_order.push('callback');
889 | test.equals(result, 2);
890 | });
891 | setTimeout(function(){
892 | test.same(call_order, [1,2,'callback',3]);
893 | test.done();
894 | }, 100);
895 | };
896 |
897 | exports['detect - mulitple matches'] = function(test){
898 | var call_order = [];
899 | async.detect([3,2,2,1,2], detectIterator.bind(this, call_order), function(result){
900 | call_order.push('callback');
901 | test.equals(result, 2);
902 | });
903 | setTimeout(function(){
904 | test.same(call_order, [1,2,'callback',2,2,3]);
905 | test.done();
906 | }, 100);
907 | };
908 |
909 | exports['detectSeries'] = function(test){
910 | var call_order = [];
911 | async.detectSeries([3,2,1], detectIterator.bind(this, call_order), function(result){
912 | call_order.push('callback');
913 | test.equals(result, 2);
914 | });
915 | setTimeout(function(){
916 | test.same(call_order, [3,2,'callback']);
917 | test.done();
918 | }, 200);
919 | };
920 |
921 | exports['detectSeries - multiple matches'] = function(test){
922 | var call_order = [];
923 | async.detectSeries([3,2,2,1,2], detectIterator.bind(this, call_order), function(result){
924 | call_order.push('callback');
925 | test.equals(result, 2);
926 | });
927 | setTimeout(function(){
928 | test.same(call_order, [3,2,'callback']);
929 | test.done();
930 | }, 200);
931 | };
932 |
933 | exports['sortBy'] = function(test){
934 | async.sortBy([{a:1},{a:15},{a:6}], function(x, callback){
935 | setTimeout(function(){callback(null, x.a);}, 0);
936 | }, function(err, result){
937 | test.same(result, [{a:1},{a:6},{a:15}]);
938 | test.done();
939 | });
940 | };
941 |
942 | exports['apply'] = function(test){
943 | test.expect(6);
944 | var fn = function(){
945 | test.same(Array.prototype.slice.call(arguments), [1,2,3,4])
946 | };
947 | async.apply(fn, 1, 2, 3, 4)();
948 | async.apply(fn, 1, 2, 3)(4);
949 | async.apply(fn, 1, 2)(3, 4);
950 | async.apply(fn, 1)(2, 3, 4);
951 | async.apply(fn)(1, 2, 3, 4);
952 | test.equals(
953 | async.apply(function(name){return 'hello ' + name}, 'world')(),
954 | 'hello world'
955 | );
956 | test.done();
957 | };
958 |
959 |
960 | // generates tests for console functions such as async.log
961 | var console_fn_tests = function(name){
962 |
963 | if (typeof console !== 'undefined') {
964 | exports[name] = function(test){
965 | test.expect(5);
966 | var fn = function(arg1, callback){
967 | test.equals(arg1, 'one');
968 | setTimeout(function(){callback(null, 'test');}, 0);
969 | };
970 | var fn_err = function(arg1, callback){
971 | test.equals(arg1, 'one');
972 | setTimeout(function(){callback('error');}, 0);
973 | };
974 | var _console_fn = console[name];
975 | var _error = console.error;
976 | console[name] = function(val){
977 | test.equals(val, 'test');
978 | test.equals(arguments.length, 1);
979 | console.error = function(val){
980 | test.equals(val, 'error');
981 | console[name] = _console_fn;
982 | console.error = _error;
983 | test.done();
984 | };
985 | async[name](fn_err, 'one');
986 | };
987 | async[name](fn, 'one');
988 | };
989 |
990 | exports[name + ' with multiple result params'] = function(test){
991 | var fn = function(callback){callback(null,'one','two','three');};
992 | var _console_fn = console[name];
993 | var called_with = [];
994 | console[name] = function(x){
995 | called_with.push(x);
996 | };
997 | async[name](fn);
998 | test.same(called_with, ['one','two','three']);
999 | console[name] = _console_fn;
1000 | test.done();
1001 | };
1002 | }
1003 |
1004 | // browser-only test
1005 | exports[name + ' without console.' + name] = function(test){
1006 | if (typeof window !== 'undefined') {
1007 | var _console = window.console;
1008 | window.console = undefined;
1009 | var fn = function(callback){callback(null, 'val');};
1010 | var fn_err = function(callback){callback('error');};
1011 | async[name](fn);
1012 | async[name](fn_err);
1013 | window.console = _console;
1014 | }
1015 | test.done();
1016 | };
1017 |
1018 | };
1019 |
1020 | console_fn_tests('log');
1021 | console_fn_tests('dir');
1022 | /*console_fn_tests('info');
1023 | console_fn_tests('warn');
1024 | console_fn_tests('error');*/
1025 |
1026 | exports['nextTick'] = function(test){
1027 | var call_order = [];
1028 | async.nextTick(function(){call_order.push('two');});
1029 | call_order.push('one');
1030 | setTimeout(function(){
1031 | test.same(call_order, ['one','two']);
1032 | test.done();
1033 | }, 50);
1034 | };
1035 |
1036 | exports['nextTick in the browser'] = function(test){
1037 | if (typeof process !== 'undefined') {
1038 | // skip this test in node
1039 | return test.done();
1040 | }
1041 | test.expect(1);
1042 |
1043 | var call_order = [];
1044 | async.nextTick(function(){call_order.push('two');});
1045 |
1046 | call_order.push('one');
1047 | setTimeout(function(){
1048 | if (typeof process !== 'undefined') {
1049 | process.nextTick = _nextTick;
1050 | }
1051 | test.same(call_order, ['one','two']);
1052 | }, 50);
1053 | setTimeout(test.done, 100);
1054 | };
1055 |
1056 | exports['noConflict - node only'] = function(test){
1057 | if (typeof process !== 'undefined') {
1058 | // node only test
1059 | test.expect(3);
1060 | var fs = require('fs');
1061 | var filename = __dirname + '/../lib/async.js';
1062 | fs.readFile(filename, function(err, content){
1063 | if(err) return test.done();
1064 |
1065 | // Script -> NodeScript in node v0.6.x
1066 | var Script = process.binding('evals').Script || process.binding('evals').NodeScript;
1067 |
1068 | var s = new Script(content, filename);
1069 | var s2 = new Script(
1070 | content + 'this.async2 = this.async.noConflict();',
1071 | filename
1072 | );
1073 |
1074 | var sandbox1 = {async: 'oldvalue'};
1075 | s.runInNewContext(sandbox1);
1076 | test.ok(sandbox1.async);
1077 |
1078 | var sandbox2 = {async: 'oldvalue'};
1079 | s2.runInNewContext(sandbox2);
1080 | test.equals(sandbox2.async, 'oldvalue');
1081 | test.ok(sandbox2.async2);
1082 |
1083 | test.done();
1084 | });
1085 | }
1086 | else test.done();
1087 | };
1088 |
1089 | exports['concat'] = function(test){
1090 | var call_order = [];
1091 | var iterator = function (x, cb) {
1092 | setTimeout(function(){
1093 | call_order.push(x);
1094 | var r = [];
1095 | while (x > 0) {
1096 | r.push(x);
1097 | x--;
1098 | }
1099 | cb(null, r);
1100 | }, x*25);
1101 | };
1102 | async.concat([1,3,2], iterator, function(err, results){
1103 | test.same(results, [1,2,1,3,2,1]);
1104 | test.same(call_order, [1,2,3]);
1105 | test.ok(!err);
1106 | test.done();
1107 | });
1108 | };
1109 |
1110 | exports['concat error'] = function(test){
1111 | var iterator = function (x, cb) {
1112 | cb(new Error('test error'));
1113 | };
1114 | async.concat([1,2,3], iterator, function(err, results){
1115 | test.ok(err);
1116 | test.done();
1117 | });
1118 | };
1119 |
1120 | exports['concatSeries'] = function(test){
1121 | var call_order = [];
1122 | var iterator = function (x, cb) {
1123 | setTimeout(function(){
1124 | call_order.push(x);
1125 | var r = [];
1126 | while (x > 0) {
1127 | r.push(x);
1128 | x--;
1129 | }
1130 | cb(null, r);
1131 | }, x*25);
1132 | };
1133 | async.concatSeries([1,3,2], iterator, function(err, results){
1134 | test.same(results, [1,3,2,1,2,1]);
1135 | test.same(call_order, [1,3,2]);
1136 | test.ok(!err);
1137 | test.done();
1138 | });
1139 | };
1140 |
1141 | exports['until'] = function (test) {
1142 | var call_order = [];
1143 |
1144 | var count = 0;
1145 | async.until(
1146 | function () {
1147 | call_order.push(['test', count]);
1148 | return (count == 5);
1149 | },
1150 | function (cb) {
1151 | call_order.push(['iterator', count]);
1152 | count++;
1153 | cb();
1154 | },
1155 | function (err) {
1156 | test.same(call_order, [
1157 | ['test', 0],
1158 | ['iterator', 0], ['test', 1],
1159 | ['iterator', 1], ['test', 2],
1160 | ['iterator', 2], ['test', 3],
1161 | ['iterator', 3], ['test', 4],
1162 | ['iterator', 4], ['test', 5],
1163 | ]);
1164 | test.equals(count, 5);
1165 | test.done();
1166 | }
1167 | );
1168 | };
1169 |
1170 | exports['whilst'] = function (test) {
1171 | var call_order = [];
1172 |
1173 | var count = 0;
1174 | async.whilst(
1175 | function () {
1176 | call_order.push(['test', count]);
1177 | return (count < 5);
1178 | },
1179 | function (cb) {
1180 | call_order.push(['iterator', count]);
1181 | count++;
1182 | cb();
1183 | },
1184 | function (err) {
1185 | test.same(call_order, [
1186 | ['test', 0],
1187 | ['iterator', 0], ['test', 1],
1188 | ['iterator', 1], ['test', 2],
1189 | ['iterator', 2], ['test', 3],
1190 | ['iterator', 3], ['test', 4],
1191 | ['iterator', 4], ['test', 5],
1192 | ]);
1193 | test.equals(count, 5);
1194 | test.done();
1195 | }
1196 | );
1197 | };
1198 |
1199 | exports['queue'] = function (test) {
1200 | var call_order = [],
1201 | delays = [160,80,240,80];
1202 |
1203 | // worker1: --1-4
1204 | // worker2: -2---3
1205 | // order of completion: 2,1,4,3
1206 |
1207 | var q = async.queue(function (task, callback) {
1208 | setTimeout(function () {
1209 | call_order.push('process ' + task);
1210 | callback('error', 'arg');
1211 | }, delays.splice(0,1)[0]);
1212 | }, 2);
1213 |
1214 | q.push(1, function (err, arg) {
1215 | test.equal(err, 'error');
1216 | test.equal(arg, 'arg');
1217 | test.equal(q.length(), 1);
1218 | call_order.push('callback ' + 1);
1219 | });
1220 | q.push(2, function (err, arg) {
1221 | test.equal(err, 'error');
1222 | test.equal(arg, 'arg');
1223 | test.equal(q.length(), 2);
1224 | call_order.push('callback ' + 2);
1225 | });
1226 | q.push(3, function (err, arg) {
1227 | test.equal(err, 'error');
1228 | test.equal(arg, 'arg');
1229 | test.equal(q.length(), 0);
1230 | call_order.push('callback ' + 3);
1231 | });
1232 | q.push(4, function (err, arg) {
1233 | test.equal(err, 'error');
1234 | test.equal(arg, 'arg');
1235 | test.equal(q.length(), 0);
1236 | call_order.push('callback ' + 4);
1237 | });
1238 | test.equal(q.length(), 4);
1239 | test.equal(q.concurrency, 2);
1240 |
1241 | setTimeout(function () {
1242 | test.same(call_order, [
1243 | 'process 2', 'callback 2',
1244 | 'process 1', 'callback 1',
1245 | 'process 4', 'callback 4',
1246 | 'process 3', 'callback 3'
1247 | ]);
1248 | test.equal(q.concurrency, 2);
1249 | test.equal(q.length(), 0);
1250 | test.done();
1251 | }, 800);
1252 | };
1253 |
1254 | exports['queue changing concurrency'] = function (test) {
1255 | var call_order = [],
1256 | delays = [40,20,60,20];
1257 |
1258 | // worker1: --1-2---3-4
1259 | // order of completion: 1,2,3,4
1260 |
1261 | var q = async.queue(function (task, callback) {
1262 | setTimeout(function () {
1263 | call_order.push('process ' + task);
1264 | callback('error', 'arg');
1265 | }, delays.splice(0,1)[0]);
1266 | }, 2);
1267 |
1268 | q.push(1, function (err, arg) {
1269 | test.equal(err, 'error');
1270 | test.equal(arg, 'arg');
1271 | test.equal(q.length(), 3);
1272 | call_order.push('callback ' + 1);
1273 | });
1274 | q.push(2, function (err, arg) {
1275 | test.equal(err, 'error');
1276 | test.equal(arg, 'arg');
1277 | test.equal(q.length(), 2);
1278 | call_order.push('callback ' + 2);
1279 | });
1280 | q.push(3, function (err, arg) {
1281 | test.equal(err, 'error');
1282 | test.equal(arg, 'arg');
1283 | test.equal(q.length(), 1);
1284 | call_order.push('callback ' + 3);
1285 | });
1286 | q.push(4, function (err, arg) {
1287 | test.equal(err, 'error');
1288 | test.equal(arg, 'arg');
1289 | test.equal(q.length(), 0);
1290 | call_order.push('callback ' + 4);
1291 | });
1292 | test.equal(q.length(), 4);
1293 | test.equal(q.concurrency, 2);
1294 | q.concurrency = 1;
1295 |
1296 | setTimeout(function () {
1297 | test.same(call_order, [
1298 | 'process 1', 'callback 1',
1299 | 'process 2', 'callback 2',
1300 | 'process 3', 'callback 3',
1301 | 'process 4', 'callback 4'
1302 | ]);
1303 | test.equal(q.concurrency, 1);
1304 | test.equal(q.length(), 0);
1305 | test.done();
1306 | }, 250);
1307 | };
1308 |
1309 | exports['queue push without callback'] = function (test) {
1310 | var call_order = [],
1311 | delays = [160,80,240,80];
1312 |
1313 | // worker1: --1-4
1314 | // worker2: -2---3
1315 | // order of completion: 2,1,4,3
1316 |
1317 | var q = async.queue(function (task, callback) {
1318 | setTimeout(function () {
1319 | call_order.push('process ' + task);
1320 | callback('error', 'arg');
1321 | }, delays.splice(0,1)[0]);
1322 | }, 2);
1323 |
1324 | q.push(1);
1325 | q.push(2);
1326 | q.push(3);
1327 | q.push(4);
1328 |
1329 | setTimeout(function () {
1330 | test.same(call_order, [
1331 | 'process 2',
1332 | 'process 1',
1333 | 'process 4',
1334 | 'process 3'
1335 | ]);
1336 | test.done();
1337 | }, 800);
1338 | };
1339 |
1340 | exports['queue bulk task'] = function (test) {
1341 | var call_order = [],
1342 | delays = [160,80,240,80];
1343 |
1344 | // worker1: --1-4
1345 | // worker2: -2---3
1346 | // order of completion: 2,1,4,3
1347 |
1348 | var q = async.queue(function (task, callback) {
1349 | setTimeout(function () {
1350 | call_order.push('process ' + task);
1351 | callback('error', task);
1352 | }, delays.splice(0,1)[0]);
1353 | }, 2);
1354 |
1355 | q.push( [1,2,3,4], function (err, arg) {
1356 | test.equal(err, 'error');
1357 | call_order.push('callback ' + arg);
1358 | });
1359 |
1360 | test.equal(q.length(), 4);
1361 | test.equal(q.concurrency, 2);
1362 |
1363 | setTimeout(function () {
1364 | test.same(call_order, [
1365 | 'process 2', 'callback 2',
1366 | 'process 1', 'callback 1',
1367 | 'process 4', 'callback 4',
1368 | 'process 3', 'callback 3'
1369 | ]);
1370 | test.equal(q.concurrency, 2);
1371 | test.equal(q.length(), 0);
1372 | test.done();
1373 | }, 800);
1374 | };
1375 |
1376 | exports['memoize'] = function (test) {
1377 | test.expect(4);
1378 | var call_order = [];
1379 |
1380 | var fn = function (arg1, arg2, callback) {
1381 | call_order.push(['fn', arg1, arg2]);
1382 | callback(null, arg1 + arg2);
1383 | };
1384 |
1385 | var fn2 = async.memoize(fn);
1386 | fn2(1, 2, function (err, result) {
1387 | test.equal(result, 3);
1388 | });
1389 | fn2(1, 2, function (err, result) {
1390 | test.equal(result, 3);
1391 | });
1392 | fn2(2, 2, function (err, result) {
1393 | test.equal(result, 4);
1394 | });
1395 |
1396 | test.same(call_order, [['fn',1,2], ['fn',2,2]]);
1397 | test.done();
1398 | };
1399 |
1400 | exports['unmemoize'] = function(test) {
1401 | test.expect(4);
1402 | var call_order = [];
1403 |
1404 | var fn = function (arg1, arg2, callback) {
1405 | call_order.push(['fn', arg1, arg2]);
1406 | callback(null, arg1 + arg2);
1407 | };
1408 |
1409 | var fn2 = async.memoize(fn);
1410 | var fn3 = async.unmemoize(fn2);
1411 | fn3(1, 2, function (err, result) {
1412 | test.equal(result, 3);
1413 | });
1414 | fn3(1, 2, function (err, result) {
1415 | test.equal(result, 3);
1416 | });
1417 | fn3(2, 2, function (err, result) {
1418 | test.equal(result, 4);
1419 | });
1420 |
1421 | test.same(call_order, [['fn',1,2], ['fn',1,2], ['fn',2,2]]);
1422 |
1423 | test.done();
1424 | }
1425 |
1426 | exports['unmemoize a not memoized function'] = function(test) {
1427 | test.expect(1);
1428 |
1429 | var fn = function (arg1, arg2, callback) {
1430 | callback(null, arg1 + arg2);
1431 | };
1432 |
1433 | var fn2 = async.unmemoize(fn);
1434 | fn2(1, 2, function(err, result) {
1435 | test.equal(result, 3);
1436 | });
1437 |
1438 | test.done();
1439 | }
1440 |
1441 | exports['memoize error'] = function (test) {
1442 | test.expect(1);
1443 | var testerr = new Error('test');
1444 | var fn = function (arg1, arg2, callback) {
1445 | callback(testerr, arg1 + arg2);
1446 | };
1447 | async.memoize(fn)(1, 2, function (err, result) {
1448 | test.equal(err, testerr);
1449 | });
1450 | test.done();
1451 | };
1452 |
1453 | exports['memoize multiple calls'] = function (test) {
1454 | test.expect(3);
1455 | var fn = function (arg1, arg2, callback) {
1456 | test.ok(true);
1457 | setTimeout(function(){
1458 | callback(null, arg1, arg2);
1459 | }, 10);
1460 | };
1461 | var fn2 = async.memoize(fn);
1462 | fn2(1, 2, function(err, result) {
1463 | test.equal(result, 1, 2);
1464 | });
1465 | fn2(1, 2, function(err, result) {
1466 | test.equal(result, 1, 2);
1467 | test.done();
1468 | });
1469 | };
1470 |
1471 | exports['memoize custom hash function'] = function (test) {
1472 | test.expect(2);
1473 | var testerr = new Error('test');
1474 |
1475 | var fn = function (arg1, arg2, callback) {
1476 | callback(testerr, arg1 + arg2);
1477 | };
1478 | var fn2 = async.memoize(fn, function () {
1479 | return 'custom hash';
1480 | });
1481 | fn2(1, 2, function (err, result) {
1482 | test.equal(result, 3);
1483 | });
1484 | fn2(2, 2, function (err, result) {
1485 | test.equal(result, 3);
1486 | });
1487 | test.done();
1488 | };
1489 |
1490 | // Issue 10 on github: https://github.com/caolan/async/issues#issue/10
1491 | exports['falsy return values in series'] = function (test) {
1492 | function taskFalse(callback) {
1493 | async.nextTick(function() {
1494 | callback(null, false);
1495 | });
1496 | };
1497 | function taskUndefined(callback) {
1498 | async.nextTick(function() {
1499 | callback(null, undefined);
1500 | });
1501 | };
1502 | function taskEmpty(callback) {
1503 | async.nextTick(function() {
1504 | callback(null);
1505 | });
1506 | };
1507 | function taskNull(callback) {
1508 | async.nextTick(function() {
1509 | callback(null, null);
1510 | });
1511 | };
1512 | async.series(
1513 | [taskFalse, taskUndefined, taskEmpty, taskNull],
1514 | function(err, results) {
1515 | test.equal(results.length, 4);
1516 | test.strictEqual(results[0], false);
1517 | test.strictEqual(results[1], undefined);
1518 | test.strictEqual(results[2], undefined);
1519 | test.strictEqual(results[3], null);
1520 | test.done();
1521 | }
1522 | );
1523 | };
1524 |
1525 | // Issue 10 on github: https://github.com/caolan/async/issues#issue/10
1526 | exports['falsy return values in parallel'] = function (test) {
1527 | function taskFalse(callback) {
1528 | async.nextTick(function() {
1529 | callback(null, false);
1530 | });
1531 | };
1532 | function taskUndefined(callback) {
1533 | async.nextTick(function() {
1534 | callback(null, undefined);
1535 | });
1536 | };
1537 | function taskEmpty(callback) {
1538 | async.nextTick(function() {
1539 | callback(null);
1540 | });
1541 | };
1542 | function taskNull(callback) {
1543 | async.nextTick(function() {
1544 | callback(null, null);
1545 | });
1546 | };
1547 | async.parallel(
1548 | [taskFalse, taskUndefined, taskEmpty, taskNull],
1549 | function(err, results) {
1550 | test.equal(results.length, 4);
1551 | test.strictEqual(results[0], false);
1552 | test.strictEqual(results[1], undefined);
1553 | test.strictEqual(results[2], undefined);
1554 | test.strictEqual(results[3], null);
1555 | test.done();
1556 | }
1557 | );
1558 | };
1559 |
1560 | exports['queue events'] = function(test) {
1561 | var calls = [];
1562 | var q = async.queue(function(task, cb) {
1563 | // nop
1564 | calls.push('process ' + task);
1565 | cb();
1566 | }, 3);
1567 |
1568 | q.saturated = function() {
1569 | test.ok(q.length() == 3, 'queue should be saturated now');
1570 | calls.push('saturated');
1571 | };
1572 | q.empty = function() {
1573 | test.ok(q.length() == 0, 'queue should be empty now');
1574 | calls.push('empty');
1575 | };
1576 | q.drain = function() {
1577 | test.ok(
1578 | q.length() == 0 && q.running() == 0,
1579 | 'queue should be empty now and no more workers should be running'
1580 | );
1581 | calls.push('drain');
1582 | test.same(calls, [
1583 | 'saturated',
1584 | 'process foo',
1585 | 'foo cb',
1586 | 'process bar',
1587 | 'bar cb',
1588 | 'process zoo',
1589 | 'zoo cb',
1590 | 'process poo',
1591 | 'poo cb',
1592 | 'empty',
1593 | 'process moo',
1594 | 'moo cb',
1595 | 'drain',
1596 | ]);
1597 | test.done();
1598 | };
1599 | q.push('foo', function () {calls.push('foo cb');});
1600 | q.push('bar', function () {calls.push('bar cb');});
1601 | q.push('zoo', function () {calls.push('zoo cb');});
1602 | q.push('poo', function () {calls.push('poo cb');});
1603 | q.push('moo', function () {calls.push('moo cb');});
1604 | };
1605 |
--------------------------------------------------------------------------------
/deps/nodeunit.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Nodeunit
3 | * https://github.com/caolan/nodeunit
4 | * Copyright (c) 2010 Caolan McMahon
5 | * MIT Licensed
6 | *
7 | * json2.js
8 | * http://www.JSON.org/json2.js
9 | * Public Domain.
10 | * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
11 | */
12 | nodeunit = (function(){
13 | /*
14 | http://www.JSON.org/json2.js
15 | 2010-11-17
16 |
17 | Public Domain.
18 |
19 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
20 |
21 | See http://www.JSON.org/js.html
22 |
23 |
24 | This code should be minified before deployment.
25 | See http://javascript.crockford.com/jsmin.html
26 |
27 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
28 | NOT CONTROL.
29 |
30 |
31 | This file creates a global JSON object containing two methods: stringify
32 | and parse.
33 |
34 | JSON.stringify(value, replacer, space)
35 | value any JavaScript value, usually an object or array.
36 |
37 | replacer an optional parameter that determines how object
38 | values are stringified for objects. It can be a
39 | function or an array of strings.
40 |
41 | space an optional parameter that specifies the indentation
42 | of nested structures. If it is omitted, the text will
43 | be packed without extra whitespace. If it is a number,
44 | it will specify the number of spaces to indent at each
45 | level. If it is a string (such as '\t' or ' '),
46 | it contains the characters used to indent at each level.
47 |
48 | This method produces a JSON text from a JavaScript value.
49 |
50 | When an object value is found, if the object contains a toJSON
51 | method, its toJSON method will be called and the result will be
52 | stringified. A toJSON method does not serialize: it returns the
53 | value represented by the name/value pair that should be serialized,
54 | or undefined if nothing should be serialized. The toJSON method
55 | will be passed the key associated with the value, and this will be
56 | bound to the value
57 |
58 | For example, this would serialize Dates as ISO strings.
59 |
60 | Date.prototype.toJSON = function (key) {
61 | function f(n) {
62 | // Format integers to have at least two digits.
63 | return n < 10 ? '0' + n : n;
64 | }
65 |
66 | return this.getUTCFullYear() + '-' +
67 | f(this.getUTCMonth() + 1) + '-' +
68 | f(this.getUTCDate()) + 'T' +
69 | f(this.getUTCHours()) + ':' +
70 | f(this.getUTCMinutes()) + ':' +
71 | f(this.getUTCSeconds()) + 'Z';
72 | };
73 |
74 | You can provide an optional replacer method. It will be passed the
75 | key and value of each member, with this bound to the containing
76 | object. The value that is returned from your method will be
77 | serialized. If your method returns undefined, then the member will
78 | be excluded from the serialization.
79 |
80 | If the replacer parameter is an array of strings, then it will be
81 | used to select the members to be serialized. It filters the results
82 | such that only members with keys listed in the replacer array are
83 | stringified.
84 |
85 | Values that do not have JSON representations, such as undefined or
86 | functions, will not be serialized. Such values in objects will be
87 | dropped; in arrays they will be replaced with null. You can use
88 | a replacer function to replace those with JSON values.
89 | JSON.stringify(undefined) returns undefined.
90 |
91 | The optional space parameter produces a stringification of the
92 | value that is filled with line breaks and indentation to make it
93 | easier to read.
94 |
95 | If the space parameter is a non-empty string, then that string will
96 | be used for indentation. If the space parameter is a number, then
97 | the indentation will be that many spaces.
98 |
99 | Example:
100 |
101 | text = JSON.stringify(['e', {pluribus: 'unum'}]);
102 | // text is '["e",{"pluribus":"unum"}]'
103 |
104 |
105 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
106 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
107 |
108 | text = JSON.stringify([new Date()], function (key, value) {
109 | return this[key] instanceof Date ?
110 | 'Date(' + this[key] + ')' : value;
111 | });
112 | // text is '["Date(---current time---)"]'
113 |
114 |
115 | JSON.parse(text, reviver)
116 | This method parses a JSON text to produce an object or array.
117 | It can throw a SyntaxError exception.
118 |
119 | The optional reviver parameter is a function that can filter and
120 | transform the results. It receives each of the keys and values,
121 | and its return value is used instead of the original value.
122 | If it returns what it received, then the structure is not modified.
123 | If it returns undefined then the member is deleted.
124 |
125 | Example:
126 |
127 | // Parse the text. Values that look like ISO date strings will
128 | // be converted to Date objects.
129 |
130 | myData = JSON.parse(text, function (key, value) {
131 | var a;
132 | if (typeof value === 'string') {
133 | a =
134 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
135 | if (a) {
136 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
137 | +a[5], +a[6]));
138 | }
139 | }
140 | return value;
141 | });
142 |
143 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
144 | var d;
145 | if (typeof value === 'string' &&
146 | value.slice(0, 5) === 'Date(' &&
147 | value.slice(-1) === ')') {
148 | d = new Date(value.slice(5, -1));
149 | if (d) {
150 | return d;
151 | }
152 | }
153 | return value;
154 | });
155 |
156 |
157 | This is a reference implementation. You are free to copy, modify, or
158 | redistribute.
159 | */
160 |
161 | /*jslint evil: true, strict: false, regexp: false */
162 |
163 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
164 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
165 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
166 | lastIndex, length, parse, prototype, push, replace, slice, stringify,
167 | test, toJSON, toString, valueOf
168 | */
169 |
170 |
171 | // Create a JSON object only if one does not already exist. We create the
172 | // methods in a closure to avoid creating global variables.
173 |
174 | if (!this.JSON) {
175 | this.JSON = {};
176 | }
177 |
178 | (function () {
179 | "use strict";
180 |
181 | function f(n) {
182 | // Format integers to have at least two digits.
183 | return n < 10 ? '0' + n : n;
184 | }
185 |
186 | if (typeof Date.prototype.toJSON !== 'function') {
187 |
188 | Date.prototype.toJSON = function (key) {
189 |
190 | return isFinite(this.valueOf()) ?
191 | this.getUTCFullYear() + '-' +
192 | f(this.getUTCMonth() + 1) + '-' +
193 | f(this.getUTCDate()) + 'T' +
194 | f(this.getUTCHours()) + ':' +
195 | f(this.getUTCMinutes()) + ':' +
196 | f(this.getUTCSeconds()) + 'Z' : null;
197 | };
198 |
199 | String.prototype.toJSON =
200 | Number.prototype.toJSON =
201 | Boolean.prototype.toJSON = function (key) {
202 | return this.valueOf();
203 | };
204 | }
205 |
206 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
207 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
208 | gap,
209 | indent,
210 | meta = { // table of character substitutions
211 | '\b': '\\b',
212 | '\t': '\\t',
213 | '\n': '\\n',
214 | '\f': '\\f',
215 | '\r': '\\r',
216 | '"' : '\\"',
217 | '\\': '\\\\'
218 | },
219 | rep;
220 |
221 |
222 | function quote(string) {
223 |
224 | // If the string contains no control characters, no quote characters, and no
225 | // backslash characters, then we can safely slap some quotes around it.
226 | // Otherwise we must also replace the offending characters with safe escape
227 | // sequences.
228 |
229 | escapable.lastIndex = 0;
230 | return escapable.test(string) ?
231 | '"' + string.replace(escapable, function (a) {
232 | var c = meta[a];
233 | return typeof c === 'string' ? c :
234 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
235 | }) + '"' :
236 | '"' + string + '"';
237 | }
238 |
239 |
240 | function str(key, holder) {
241 |
242 | // Produce a string from holder[key].
243 |
244 | var i, // The loop counter.
245 | k, // The member key.
246 | v, // The member value.
247 | length,
248 | mind = gap,
249 | partial,
250 | value = holder[key];
251 |
252 | // If the value has a toJSON method, call it to obtain a replacement value.
253 |
254 | if (value && typeof value === 'object' &&
255 | typeof value.toJSON === 'function') {
256 | value = value.toJSON(key);
257 | }
258 |
259 | // If we were called with a replacer function, then call the replacer to
260 | // obtain a replacement value.
261 |
262 | if (typeof rep === 'function') {
263 | value = rep.call(holder, key, value);
264 | }
265 |
266 | // What happens next depends on the value's type.
267 |
268 | switch (typeof value) {
269 | case 'string':
270 | return quote(value);
271 |
272 | case 'number':
273 |
274 | // JSON numbers must be finite. Encode non-finite numbers as null.
275 |
276 | return isFinite(value) ? String(value) : 'null';
277 |
278 | case 'boolean':
279 | case 'null':
280 |
281 | // If the value is a boolean or null, convert it to a string. Note:
282 | // typeof null does not produce 'null'. The case is included here in
283 | // the remote chance that this gets fixed someday.
284 |
285 | return String(value);
286 |
287 | // If the type is 'object', we might be dealing with an object or an array or
288 | // null.
289 |
290 | case 'object':
291 |
292 | // Due to a specification blunder in ECMAScript, typeof null is 'object',
293 | // so watch out for that case.
294 |
295 | if (!value) {
296 | return 'null';
297 | }
298 |
299 | // Make an array to hold the partial results of stringifying this object value.
300 |
301 | gap += indent;
302 | partial = [];
303 |
304 | // Is the value an array?
305 |
306 | if (Object.prototype.toString.apply(value) === '[object Array]') {
307 |
308 | // The value is an array. Stringify every element. Use null as a placeholder
309 | // for non-JSON values.
310 |
311 | length = value.length;
312 | for (i = 0; i < length; i += 1) {
313 | partial[i] = str(i, value) || 'null';
314 | }
315 |
316 | // Join all of the elements together, separated with commas, and wrap them in
317 | // brackets.
318 |
319 | v = partial.length === 0 ? '[]' :
320 | gap ? '[\n' + gap +
321 | partial.join(',\n' + gap) + '\n' +
322 | mind + ']' :
323 | '[' + partial.join(',') + ']';
324 | gap = mind;
325 | return v;
326 | }
327 |
328 | // If the replacer is an array, use it to select the members to be stringified.
329 |
330 | if (rep && typeof rep === 'object') {
331 | length = rep.length;
332 | for (i = 0; i < length; i += 1) {
333 | k = rep[i];
334 | if (typeof k === 'string') {
335 | v = str(k, value);
336 | if (v) {
337 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
338 | }
339 | }
340 | }
341 | } else {
342 |
343 | // Otherwise, iterate through all of the keys in the object.
344 |
345 | for (k in value) {
346 | if (Object.hasOwnProperty.call(value, k)) {
347 | v = str(k, value);
348 | if (v) {
349 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
350 | }
351 | }
352 | }
353 | }
354 |
355 | // Join all of the member texts together, separated with commas,
356 | // and wrap them in braces.
357 |
358 | v = partial.length === 0 ? '{}' :
359 | gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
360 | mind + '}' : '{' + partial.join(',') + '}';
361 | gap = mind;
362 | return v;
363 | }
364 | }
365 |
366 | // If the JSON object does not yet have a stringify method, give it one.
367 |
368 | if (typeof JSON.stringify !== 'function') {
369 | JSON.stringify = function (value, replacer, space) {
370 |
371 | // The stringify method takes a value and an optional replacer, and an optional
372 | // space parameter, and returns a JSON text. The replacer can be a function
373 | // that can replace values, or an array of strings that will select the keys.
374 | // A default replacer method can be provided. Use of the space parameter can
375 | // produce text that is more easily readable.
376 |
377 | var i;
378 | gap = '';
379 | indent = '';
380 |
381 | // If the space parameter is a number, make an indent string containing that
382 | // many spaces.
383 |
384 | if (typeof space === 'number') {
385 | for (i = 0; i < space; i += 1) {
386 | indent += ' ';
387 | }
388 |
389 | // If the space parameter is a string, it will be used as the indent string.
390 |
391 | } else if (typeof space === 'string') {
392 | indent = space;
393 | }
394 |
395 | // If there is a replacer, it must be a function or an array.
396 | // Otherwise, throw an error.
397 |
398 | rep = replacer;
399 | if (replacer && typeof replacer !== 'function' &&
400 | (typeof replacer !== 'object' ||
401 | typeof replacer.length !== 'number')) {
402 | throw new Error('JSON.stringify');
403 | }
404 |
405 | // Make a fake root object containing our value under the key of ''.
406 | // Return the result of stringifying the value.
407 |
408 | return str('', {'': value});
409 | };
410 | }
411 |
412 |
413 | // If the JSON object does not yet have a parse method, give it one.
414 |
415 | if (typeof JSON.parse !== 'function') {
416 | JSON.parse = function (text, reviver) {
417 |
418 | // The parse method takes a text and an optional reviver function, and returns
419 | // a JavaScript value if the text is a valid JSON text.
420 |
421 | var j;
422 |
423 | function walk(holder, key) {
424 |
425 | // The walk method is used to recursively walk the resulting structure so
426 | // that modifications can be made.
427 |
428 | var k, v, value = holder[key];
429 | if (value && typeof value === 'object') {
430 | for (k in value) {
431 | if (Object.hasOwnProperty.call(value, k)) {
432 | v = walk(value, k);
433 | if (v !== undefined) {
434 | value[k] = v;
435 | } else {
436 | delete value[k];
437 | }
438 | }
439 | }
440 | }
441 | return reviver.call(holder, key, value);
442 | }
443 |
444 |
445 | // Parsing happens in four stages. In the first stage, we replace certain
446 | // Unicode characters with escape sequences. JavaScript handles many characters
447 | // incorrectly, either silently deleting them, or treating them as line endings.
448 |
449 | text = String(text);
450 | cx.lastIndex = 0;
451 | if (cx.test(text)) {
452 | text = text.replace(cx, function (a) {
453 | return '\\u' +
454 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
455 | });
456 | }
457 |
458 | // In the second stage, we run the text against regular expressions that look
459 | // for non-JSON patterns. We are especially concerned with '()' and 'new'
460 | // because they can cause invocation, and '=' because it can cause mutation.
461 | // But just to be safe, we want to reject all unexpected forms.
462 |
463 | // We split the second stage into 4 regexp operations in order to work around
464 | // crippling inefficiencies in IE's and Safari's regexp engines. First we
465 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
466 | // replace all simple value tokens with ']' characters. Third, we delete all
467 | // open brackets that follow a colon or comma or that begin the text. Finally,
468 | // we look to see that the remaining characters are only whitespace or ']' or
469 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
470 |
471 | if (/^[\],:{}\s]*$/
472 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
473 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
474 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
475 |
476 | // In the third stage we use the eval function to compile the text into a
477 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
478 | // in JavaScript: it can begin a block or an object literal. We wrap the text
479 | // in parens to eliminate the ambiguity.
480 |
481 | j = eval('(' + text + ')');
482 |
483 | // In the optional fourth stage, we recursively walk the new structure, passing
484 | // each name/value pair to a reviver function for possible transformation.
485 |
486 | return typeof reviver === 'function' ?
487 | walk({'': j}, '') : j;
488 | }
489 |
490 | // If the text is not JSON parseable, then a SyntaxError is thrown.
491 |
492 | throw new SyntaxError('JSON.parse');
493 | };
494 | }
495 | }());
496 | var assert = this.assert = {};
497 | var types = {};
498 | var core = {};
499 | var nodeunit = {};
500 | var reporter = {};
501 | /*global setTimeout: false, console: false */
502 | (function () {
503 |
504 | var async = {};
505 |
506 | // global on the server, window in the browser
507 | var root = this,
508 | previous_async = root.async;
509 |
510 | if (typeof module !== 'undefined' && module.exports) {
511 | module.exports = async;
512 | }
513 | else {
514 | root.async = async;
515 | }
516 |
517 | async.noConflict = function () {
518 | root.async = previous_async;
519 | return async;
520 | };
521 |
522 | //// cross-browser compatiblity functions ////
523 |
524 | var _forEach = function (arr, iterator) {
525 | if (arr.forEach) {
526 | return arr.forEach(iterator);
527 | }
528 | for (var i = 0; i < arr.length; i += 1) {
529 | iterator(arr[i], i, arr);
530 | }
531 | };
532 |
533 | var _map = function (arr, iterator) {
534 | if (arr.map) {
535 | return arr.map(iterator);
536 | }
537 | var results = [];
538 | _forEach(arr, function (x, i, a) {
539 | results.push(iterator(x, i, a));
540 | });
541 | return results;
542 | };
543 |
544 | var _reduce = function (arr, iterator, memo) {
545 | if (arr.reduce) {
546 | return arr.reduce(iterator, memo);
547 | }
548 | _forEach(arr, function (x, i, a) {
549 | memo = iterator(memo, x, i, a);
550 | });
551 | return memo;
552 | };
553 |
554 | var _keys = function (obj) {
555 | if (Object.keys) {
556 | return Object.keys(obj);
557 | }
558 | var keys = [];
559 | for (var k in obj) {
560 | if (obj.hasOwnProperty(k)) {
561 | keys.push(k);
562 | }
563 | }
564 | return keys;
565 | };
566 |
567 | var _indexOf = function (arr, item) {
568 | if (arr.indexOf) {
569 | return arr.indexOf(item);
570 | }
571 | for (var i = 0; i < arr.length; i += 1) {
572 | if (arr[i] === item) {
573 | return i;
574 | }
575 | }
576 | return -1;
577 | };
578 |
579 | //// exported async module functions ////
580 |
581 | //// nextTick implementation with browser-compatible fallback ////
582 | async.nextTick = function (fn) {
583 | if (typeof process === 'undefined' || !(process.nextTick)) {
584 | setTimeout(fn, 0);
585 | }
586 | else {
587 | process.nextTick(fn);
588 | }
589 | };
590 |
591 | async.forEach = function (arr, iterator, callback) {
592 | if (!arr.length) {
593 | return callback();
594 | }
595 | var completed = 0;
596 | _forEach(arr, function (x) {
597 | iterator(x, function (err) {
598 | if (err) {
599 | callback(err);
600 | callback = function () {};
601 | }
602 | else {
603 | completed += 1;
604 | if (completed === arr.length) {
605 | callback();
606 | }
607 | }
608 | });
609 | });
610 | };
611 |
612 | async.forEachSeries = function (arr, iterator, callback) {
613 | if (!arr.length) {
614 | return callback();
615 | }
616 | var completed = 0;
617 | var iterate = function () {
618 | iterator(arr[completed], function (err) {
619 | if (err) {
620 | callback(err);
621 | callback = function () {};
622 | }
623 | else {
624 | completed += 1;
625 | if (completed === arr.length) {
626 | callback();
627 | }
628 | else {
629 | iterate();
630 | }
631 | }
632 | });
633 | };
634 | iterate();
635 | };
636 |
637 |
638 | var doParallel = function (fn) {
639 | return function () {
640 | var args = Array.prototype.slice.call(arguments);
641 | return fn.apply(null, [async.forEach].concat(args));
642 | };
643 | };
644 | var doSeries = function (fn) {
645 | return function () {
646 | var args = Array.prototype.slice.call(arguments);
647 | return fn.apply(null, [async.forEachSeries].concat(args));
648 | };
649 | };
650 |
651 |
652 | var _asyncMap = function (eachfn, arr, iterator, callback) {
653 | var results = [];
654 | arr = _map(arr, function (x, i) {
655 | return {index: i, value: x};
656 | });
657 | eachfn(arr, function (x, callback) {
658 | iterator(x.value, function (err, v) {
659 | results[x.index] = v;
660 | callback(err);
661 | });
662 | }, function (err) {
663 | callback(err, results);
664 | });
665 | };
666 | async.map = doParallel(_asyncMap);
667 | async.mapSeries = doSeries(_asyncMap);
668 |
669 |
670 | // reduce only has a series version, as doing reduce in parallel won't
671 | // work in many situations.
672 | async.reduce = function (arr, memo, iterator, callback) {
673 | async.forEachSeries(arr, function (x, callback) {
674 | iterator(memo, x, function (err, v) {
675 | memo = v;
676 | callback(err);
677 | });
678 | }, function (err) {
679 | callback(err, memo);
680 | });
681 | };
682 | // inject alias
683 | async.inject = async.reduce;
684 | // foldl alias
685 | async.foldl = async.reduce;
686 |
687 | async.reduceRight = function (arr, memo, iterator, callback) {
688 | var reversed = _map(arr, function (x) {
689 | return x;
690 | }).reverse();
691 | async.reduce(reversed, memo, iterator, callback);
692 | };
693 | // foldr alias
694 | async.foldr = async.reduceRight;
695 |
696 | var _filter = function (eachfn, arr, iterator, callback) {
697 | var results = [];
698 | arr = _map(arr, function (x, i) {
699 | return {index: i, value: x};
700 | });
701 | eachfn(arr, function (x, callback) {
702 | iterator(x.value, function (v) {
703 | if (v) {
704 | results.push(x);
705 | }
706 | callback();
707 | });
708 | }, function (err) {
709 | callback(_map(results.sort(function (a, b) {
710 | return a.index - b.index;
711 | }), function (x) {
712 | return x.value;
713 | }));
714 | });
715 | };
716 | async.filter = doParallel(_filter);
717 | async.filterSeries = doSeries(_filter);
718 | // select alias
719 | async.select = async.filter;
720 | async.selectSeries = async.filterSeries;
721 |
722 | var _reject = function (eachfn, arr, iterator, callback) {
723 | var results = [];
724 | arr = _map(arr, function (x, i) {
725 | return {index: i, value: x};
726 | });
727 | eachfn(arr, function (x, callback) {
728 | iterator(x.value, function (v) {
729 | if (!v) {
730 | results.push(x);
731 | }
732 | callback();
733 | });
734 | }, function (err) {
735 | callback(_map(results.sort(function (a, b) {
736 | return a.index - b.index;
737 | }), function (x) {
738 | return x.value;
739 | }));
740 | });
741 | };
742 | async.reject = doParallel(_reject);
743 | async.rejectSeries = doSeries(_reject);
744 |
745 | var _detect = function (eachfn, arr, iterator, main_callback) {
746 | eachfn(arr, function (x, callback) {
747 | iterator(x, function (result) {
748 | if (result) {
749 | main_callback(x);
750 | }
751 | else {
752 | callback();
753 | }
754 | });
755 | }, function (err) {
756 | main_callback();
757 | });
758 | };
759 | async.detect = doParallel(_detect);
760 | async.detectSeries = doSeries(_detect);
761 |
762 | async.some = function (arr, iterator, main_callback) {
763 | async.forEach(arr, function (x, callback) {
764 | iterator(x, function (v) {
765 | if (v) {
766 | main_callback(true);
767 | main_callback = function () {};
768 | }
769 | callback();
770 | });
771 | }, function (err) {
772 | main_callback(false);
773 | });
774 | };
775 | // any alias
776 | async.any = async.some;
777 |
778 | async.every = function (arr, iterator, main_callback) {
779 | async.forEach(arr, function (x, callback) {
780 | iterator(x, function (v) {
781 | if (!v) {
782 | main_callback(false);
783 | main_callback = function () {};
784 | }
785 | callback();
786 | });
787 | }, function (err) {
788 | main_callback(true);
789 | });
790 | };
791 | // all alias
792 | async.all = async.every;
793 |
794 | async.sortBy = function (arr, iterator, callback) {
795 | async.map(arr, function (x, callback) {
796 | iterator(x, function (err, criteria) {
797 | if (err) {
798 | callback(err);
799 | }
800 | else {
801 | callback(null, {value: x, criteria: criteria});
802 | }
803 | });
804 | }, function (err, results) {
805 | if (err) {
806 | return callback(err);
807 | }
808 | else {
809 | var fn = function (left, right) {
810 | var a = left.criteria, b = right.criteria;
811 | return a < b ? -1 : a > b ? 1 : 0;
812 | };
813 | callback(null, _map(results.sort(fn), function (x) {
814 | return x.value;
815 | }));
816 | }
817 | });
818 | };
819 |
820 | async.auto = function (tasks, callback) {
821 | callback = callback || function () {};
822 | var keys = _keys(tasks);
823 | if (!keys.length) {
824 | return callback(null);
825 | }
826 |
827 | var completed = [];
828 |
829 | var listeners = [];
830 | var addListener = function (fn) {
831 | listeners.unshift(fn);
832 | };
833 | var removeListener = function (fn) {
834 | for (var i = 0; i < listeners.length; i += 1) {
835 | if (listeners[i] === fn) {
836 | listeners.splice(i, 1);
837 | return;
838 | }
839 | }
840 | };
841 | var taskComplete = function () {
842 | _forEach(listeners, function (fn) {
843 | fn();
844 | });
845 | };
846 |
847 | addListener(function () {
848 | if (completed.length === keys.length) {
849 | callback(null);
850 | }
851 | });
852 |
853 | _forEach(keys, function (k) {
854 | var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
855 | var taskCallback = function (err) {
856 | if (err) {
857 | callback(err);
858 | // stop subsequent errors hitting callback multiple times
859 | callback = function () {};
860 | }
861 | else {
862 | completed.push(k);
863 | taskComplete();
864 | }
865 | };
866 | var requires = task.slice(0, Math.abs(task.length - 1)) || [];
867 | var ready = function () {
868 | return _reduce(requires, function (a, x) {
869 | return (a && _indexOf(completed, x) !== -1);
870 | }, true);
871 | };
872 | if (ready()) {
873 | task[task.length - 1](taskCallback);
874 | }
875 | else {
876 | var listener = function () {
877 | if (ready()) {
878 | removeListener(listener);
879 | task[task.length - 1](taskCallback);
880 | }
881 | };
882 | addListener(listener);
883 | }
884 | });
885 | };
886 |
887 | async.waterfall = function (tasks, callback) {
888 | if (!tasks.length) {
889 | return callback();
890 | }
891 | callback = callback || function () {};
892 | var wrapIterator = function (iterator) {
893 | return function (err) {
894 | if (err) {
895 | callback(err);
896 | callback = function () {};
897 | }
898 | else {
899 | var args = Array.prototype.slice.call(arguments, 1);
900 | var next = iterator.next();
901 | if (next) {
902 | args.push(wrapIterator(next));
903 | }
904 | else {
905 | args.push(callback);
906 | }
907 | async.nextTick(function () {
908 | iterator.apply(null, args);
909 | });
910 | }
911 | };
912 | };
913 | wrapIterator(async.iterator(tasks))();
914 | };
915 |
916 | async.parallel = function (tasks, callback) {
917 | callback = callback || function () {};
918 | if (tasks.constructor === Array) {
919 | async.map(tasks, function (fn, callback) {
920 | if (fn) {
921 | fn(function (err) {
922 | var args = Array.prototype.slice.call(arguments, 1);
923 | if (args.length <= 1) {
924 | args = args[0];
925 | }
926 | callback.call(null, err, args || null);
927 | });
928 | }
929 | }, callback);
930 | }
931 | else {
932 | var results = {};
933 | async.forEach(_keys(tasks), function (k, callback) {
934 | tasks[k](function (err) {
935 | var args = Array.prototype.slice.call(arguments, 1);
936 | if (args.length <= 1) {
937 | args = args[0];
938 | }
939 | results[k] = args;
940 | callback(err);
941 | });
942 | }, function (err) {
943 | callback(err, results);
944 | });
945 | }
946 | };
947 |
948 | async.series = function (tasks, callback) {
949 | callback = callback || function () {};
950 | if (tasks.constructor === Array) {
951 | async.mapSeries(tasks, function (fn, callback) {
952 | if (fn) {
953 | fn(function (err) {
954 | var args = Array.prototype.slice.call(arguments, 1);
955 | if (args.length <= 1) {
956 | args = args[0];
957 | }
958 | callback.call(null, err, args || null);
959 | });
960 | }
961 | }, callback);
962 | }
963 | else {
964 | var results = {};
965 | async.forEachSeries(_keys(tasks), function (k, callback) {
966 | tasks[k](function (err) {
967 | var args = Array.prototype.slice.call(arguments, 1);
968 | if (args.length <= 1) {
969 | args = args[0];
970 | }
971 | results[k] = args;
972 | callback(err);
973 | });
974 | }, function (err) {
975 | callback(err, results);
976 | });
977 | }
978 | };
979 |
980 | async.iterator = function (tasks) {
981 | var makeCallback = function (index) {
982 | var fn = function () {
983 | if (tasks.length) {
984 | tasks[index].apply(null, arguments);
985 | }
986 | return fn.next();
987 | };
988 | fn.next = function () {
989 | return (index < tasks.length - 1) ? makeCallback(index + 1): null;
990 | };
991 | return fn;
992 | };
993 | return makeCallback(0);
994 | };
995 |
996 | async.apply = function (fn) {
997 | var args = Array.prototype.slice.call(arguments, 1);
998 | return function () {
999 | return fn.apply(
1000 | null, args.concat(Array.prototype.slice.call(arguments))
1001 | );
1002 | };
1003 | };
1004 |
1005 | var _concat = function (eachfn, arr, fn, callback) {
1006 | var r = [];
1007 | eachfn(arr, function (x, cb) {
1008 | fn(x, function (err, y) {
1009 | r = r.concat(y || []);
1010 | cb(err);
1011 | });
1012 | }, function (err) {
1013 | callback(err, r);
1014 | });
1015 | };
1016 | async.concat = doParallel(_concat);
1017 | async.concatSeries = doSeries(_concat);
1018 |
1019 | async.whilst = function (test, iterator, callback) {
1020 | if (test()) {
1021 | iterator(function (err) {
1022 | if (err) {
1023 | return callback(err);
1024 | }
1025 | async.whilst(test, iterator, callback);
1026 | });
1027 | }
1028 | else {
1029 | callback();
1030 | }
1031 | };
1032 |
1033 | async.until = function (test, iterator, callback) {
1034 | if (!test()) {
1035 | iterator(function (err) {
1036 | if (err) {
1037 | return callback(err);
1038 | }
1039 | async.until(test, iterator, callback);
1040 | });
1041 | }
1042 | else {
1043 | callback();
1044 | }
1045 | };
1046 |
1047 | async.queue = function (worker, concurrency) {
1048 | var workers = 0;
1049 | var tasks = [];
1050 | var q = {
1051 | concurrency: concurrency,
1052 | push: function (data, callback) {
1053 | tasks.push({data: data, callback: callback});
1054 | async.nextTick(q.process);
1055 | },
1056 | process: function () {
1057 | if (workers < q.concurrency && tasks.length) {
1058 | var task = tasks.splice(0, 1)[0];
1059 | workers += 1;
1060 | worker(task.data, function () {
1061 | workers -= 1;
1062 | if (task.callback) {
1063 | task.callback.apply(task, arguments);
1064 | }
1065 | q.process();
1066 | });
1067 | }
1068 | },
1069 | length: function () {
1070 | return tasks.length;
1071 | }
1072 | };
1073 | return q;
1074 | };
1075 |
1076 | var _console_fn = function (name) {
1077 | return function (fn) {
1078 | var args = Array.prototype.slice.call(arguments, 1);
1079 | fn.apply(null, args.concat([function (err) {
1080 | var args = Array.prototype.slice.call(arguments, 1);
1081 | if (typeof console !== 'undefined') {
1082 | if (err) {
1083 | if (console.error) {
1084 | console.error(err);
1085 | }
1086 | }
1087 | else if (console[name]) {
1088 | _forEach(args, function (x) {
1089 | console[name](x);
1090 | });
1091 | }
1092 | }
1093 | }]));
1094 | };
1095 | };
1096 | async.log = _console_fn('log');
1097 | async.dir = _console_fn('dir');
1098 | /*async.info = _console_fn('info');
1099 | async.warn = _console_fn('warn');
1100 | async.error = _console_fn('error');*/
1101 |
1102 | }());
1103 | (function(exports){
1104 | /**
1105 | * This file is based on the node.js assert module, but with some small
1106 | * changes for browser-compatibility
1107 | * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
1108 | */
1109 |
1110 |
1111 | /**
1112 | * Added for browser compatibility
1113 | */
1114 |
1115 | var _keys = function(obj){
1116 | if(Object.keys) return Object.keys(obj);
1117 | var keys = [];
1118 | for(var k in obj){
1119 | if(obj.hasOwnProperty(k)) keys.push(k);
1120 | }
1121 | return keys;
1122 | };
1123 |
1124 |
1125 |
1126 | // http://wiki.commonjs.org/wiki/Unit_Testing/1.0
1127 | //
1128 | // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
1129 | //
1130 | // Originally from narwhal.js (http://narwhaljs.org)
1131 | // Copyright (c) 2009 Thomas Robinson <280north.com>
1132 | //
1133 | // Permission is hereby granted, free of charge, to any person obtaining a copy
1134 | // of this software and associated documentation files (the 'Software'), to
1135 | // deal in the Software without restriction, including without limitation the
1136 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
1137 | // sell copies of the Software, and to permit persons to whom the Software is
1138 | // furnished to do so, subject to the following conditions:
1139 | //
1140 | // The above copyright notice and this permission notice shall be included in
1141 | // all copies or substantial portions of the Software.
1142 | //
1143 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1144 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1145 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1146 | // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1147 | // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1148 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1149 |
1150 |
1151 | var pSlice = Array.prototype.slice;
1152 |
1153 | // 1. The assert module provides functions that throw
1154 | // AssertionError's when particular conditions are not met. The
1155 | // assert module must conform to the following interface.
1156 |
1157 | var assert = exports;
1158 |
1159 | // 2. The AssertionError is defined in assert.
1160 | // new assert.AssertionError({message: message, actual: actual, expected: expected})
1161 |
1162 | assert.AssertionError = function AssertionError (options) {
1163 | this.name = "AssertionError";
1164 | this.message = options.message;
1165 | this.actual = options.actual;
1166 | this.expected = options.expected;
1167 | this.operator = options.operator;
1168 | var stackStartFunction = options.stackStartFunction || fail;
1169 |
1170 | if (Error.captureStackTrace) {
1171 | Error.captureStackTrace(this, stackStartFunction);
1172 | }
1173 | };
1174 | // code from util.inherits in node
1175 | assert.AssertionError.super_ = Error;
1176 |
1177 |
1178 | // EDITED FOR BROWSER COMPATIBILITY: replaced Object.create call
1179 | // TODO: test what effect this may have
1180 | var ctor = function () { this.constructor = assert.AssertionError; };
1181 | ctor.prototype = Error.prototype;
1182 | assert.AssertionError.prototype = new ctor();
1183 |
1184 |
1185 | assert.AssertionError.prototype.toString = function() {
1186 | if (this.message) {
1187 | return [this.name+":", this.message].join(' ');
1188 | } else {
1189 | return [ this.name+":"
1190 | , JSON.stringify(this.expected )
1191 | , this.operator
1192 | , JSON.stringify(this.actual)
1193 | ].join(" ");
1194 | }
1195 | };
1196 |
1197 | // assert.AssertionError instanceof Error
1198 |
1199 | assert.AssertionError.__proto__ = Error.prototype;
1200 |
1201 | // At present only the three keys mentioned above are used and
1202 | // understood by the spec. Implementations or sub modules can pass
1203 | // other keys to the AssertionError's constructor - they will be
1204 | // ignored.
1205 |
1206 | // 3. All of the following functions must throw an AssertionError
1207 | // when a corresponding condition is not met, with a message that
1208 | // may be undefined if not provided. All assertion methods provide
1209 | // both the actual and expected values to the assertion error for
1210 | // display purposes.
1211 |
1212 | function fail(actual, expected, message, operator, stackStartFunction) {
1213 | throw new assert.AssertionError({
1214 | message: message,
1215 | actual: actual,
1216 | expected: expected,
1217 | operator: operator,
1218 | stackStartFunction: stackStartFunction
1219 | });
1220 | }
1221 |
1222 | // EXTENSION! allows for well behaved errors defined elsewhere.
1223 | assert.fail = fail;
1224 |
1225 | // 4. Pure assertion tests whether a value is truthy, as determined
1226 | // by !!guard.
1227 | // assert.ok(guard, message_opt);
1228 | // This statement is equivalent to assert.equal(true, guard,
1229 | // message_opt);. To test strictly for the value true, use
1230 | // assert.strictEqual(true, guard, message_opt);.
1231 |
1232 | assert.ok = function ok(value, message) {
1233 | if (!!!value) fail(value, true, message, "==", assert.ok);
1234 | };
1235 |
1236 | // 5. The equality assertion tests shallow, coercive equality with
1237 | // ==.
1238 | // assert.equal(actual, expected, message_opt);
1239 |
1240 | assert.equal = function equal(actual, expected, message) {
1241 | if (actual != expected) fail(actual, expected, message, "==", assert.equal);
1242 | };
1243 |
1244 | // 6. The non-equality assertion tests for whether two objects are not equal
1245 | // with != assert.notEqual(actual, expected, message_opt);
1246 |
1247 | assert.notEqual = function notEqual(actual, expected, message) {
1248 | if (actual == expected) {
1249 | fail(actual, expected, message, "!=", assert.notEqual);
1250 | }
1251 | };
1252 |
1253 | // 7. The equivalence assertion tests a deep equality relation.
1254 | // assert.deepEqual(actual, expected, message_opt);
1255 |
1256 | assert.deepEqual = function deepEqual(actual, expected, message) {
1257 | if (!_deepEqual(actual, expected)) {
1258 | fail(actual, expected, message, "deepEqual", assert.deepEqual);
1259 | }
1260 | };
1261 |
1262 | function _deepEqual(actual, expected) {
1263 | // 7.1. All identical values are equivalent, as determined by ===.
1264 | if (actual === expected) {
1265 | return true;
1266 | // 7.2. If the expected value is a Date object, the actual value is
1267 | // equivalent if it is also a Date object that refers to the same time.
1268 | } else if (actual instanceof Date && expected instanceof Date) {
1269 | return actual.getTime() === expected.getTime();
1270 |
1271 | // 7.3. Other pairs that do not both pass typeof value == "object",
1272 | // equivalence is determined by ==.
1273 | } else if (typeof actual != 'object' && typeof expected != 'object') {
1274 | return actual == expected;
1275 |
1276 | // 7.4. For all other Object pairs, including Array objects, equivalence is
1277 | // determined by having the same number of owned properties (as verified
1278 | // with Object.prototype.hasOwnProperty.call), the same set of keys
1279 | // (although not necessarily the same order), equivalent values for every
1280 | // corresponding key, and an identical "prototype" property. Note: this
1281 | // accounts for both named and indexed properties on Arrays.
1282 | } else {
1283 | return objEquiv(actual, expected);
1284 | }
1285 | }
1286 |
1287 | function isUndefinedOrNull (value) {
1288 | return value === null || value === undefined;
1289 | }
1290 |
1291 | function isArguments (object) {
1292 | return Object.prototype.toString.call(object) == '[object Arguments]';
1293 | }
1294 |
1295 | function objEquiv (a, b) {
1296 | if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
1297 | return false;
1298 | // an identical "prototype" property.
1299 | if (a.prototype !== b.prototype) return false;
1300 | //~~~I've managed to break Object.keys through screwy arguments passing.
1301 | // Converting to array solves the problem.
1302 | if (isArguments(a)) {
1303 | if (!isArguments(b)) {
1304 | return false;
1305 | }
1306 | a = pSlice.call(a);
1307 | b = pSlice.call(b);
1308 | return _deepEqual(a, b);
1309 | }
1310 | try{
1311 | var ka = _keys(a),
1312 | kb = _keys(b),
1313 | key, i;
1314 | } catch (e) {//happens when one is a string literal and the other isn't
1315 | return false;
1316 | }
1317 | // having the same number of owned properties (keys incorporates hasOwnProperty)
1318 | if (ka.length != kb.length)
1319 | return false;
1320 | //the same set of keys (although not necessarily the same order),
1321 | ka.sort();
1322 | kb.sort();
1323 | //~~~cheap key test
1324 | for (i = ka.length - 1; i >= 0; i--) {
1325 | if (ka[i] != kb[i])
1326 | return false;
1327 | }
1328 | //equivalent values for every corresponding key, and
1329 | //~~~possibly expensive deep test
1330 | for (i = ka.length - 1; i >= 0; i--) {
1331 | key = ka[i];
1332 | if (!_deepEqual(a[key], b[key] ))
1333 | return false;
1334 | }
1335 | return true;
1336 | }
1337 |
1338 | // 8. The non-equivalence assertion tests for any deep inequality.
1339 | // assert.notDeepEqual(actual, expected, message_opt);
1340 |
1341 | assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
1342 | if (_deepEqual(actual, expected)) {
1343 | fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual);
1344 | }
1345 | };
1346 |
1347 | // 9. The strict equality assertion tests strict equality, as determined by ===.
1348 | // assert.strictEqual(actual, expected, message_opt);
1349 |
1350 | assert.strictEqual = function strictEqual(actual, expected, message) {
1351 | if (actual !== expected) {
1352 | fail(actual, expected, message, "===", assert.strictEqual);
1353 | }
1354 | };
1355 |
1356 | // 10. The strict non-equality assertion tests for strict inequality, as determined by !==.
1357 | // assert.notStrictEqual(actual, expected, message_opt);
1358 |
1359 | assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
1360 | if (actual === expected) {
1361 | fail(actual, expected, message, "!==", assert.notStrictEqual);
1362 | }
1363 | };
1364 |
1365 | function _throws (shouldThrow, block, err, message) {
1366 | var exception = null,
1367 | threw = false,
1368 | typematters = true;
1369 |
1370 | message = message || "";
1371 |
1372 | //handle optional arguments
1373 | if (arguments.length == 3) {
1374 | if (typeof(err) == "string") {
1375 | message = err;
1376 | typematters = false;
1377 | }
1378 | } else if (arguments.length == 2) {
1379 | typematters = false;
1380 | }
1381 |
1382 | try {
1383 | block();
1384 | } catch (e) {
1385 | threw = true;
1386 | exception = e;
1387 | }
1388 |
1389 | if (shouldThrow && !threw) {
1390 | fail( "Missing expected exception"
1391 | + (err && err.name ? " ("+err.name+")." : '.')
1392 | + (message ? " " + message : "")
1393 | );
1394 | }
1395 | if (!shouldThrow && threw && typematters && exception instanceof err) {
1396 | fail( "Got unwanted exception"
1397 | + (err && err.name ? " ("+err.name+")." : '.')
1398 | + (message ? " " + message : "")
1399 | );
1400 | }
1401 | if ((shouldThrow && threw && typematters && !(exception instanceof err)) ||
1402 | (!shouldThrow && threw)) {
1403 | throw exception;
1404 | }
1405 | };
1406 |
1407 | // 11. Expected to throw an error:
1408 | // assert.throws(block, Error_opt, message_opt);
1409 |
1410 | assert.throws = function(block, /*optional*/error, /*optional*/message) {
1411 | _throws.apply(this, [true].concat(pSlice.call(arguments)));
1412 | };
1413 |
1414 | // EXTENSION! This is annoying to write outside this module.
1415 | assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
1416 | _throws.apply(this, [false].concat(pSlice.call(arguments)));
1417 | };
1418 |
1419 | assert.ifError = function (err) { if (err) {throw err;}};
1420 | })(assert);
1421 | (function(exports){
1422 | /*!
1423 | * Nodeunit
1424 | * Copyright (c) 2010 Caolan McMahon
1425 | * MIT Licensed
1426 | *
1427 | * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
1428 | * Only code on that line will be removed, its mostly to avoid requiring code
1429 | * that is node specific
1430 | */
1431 |
1432 | /**
1433 | * Module dependencies
1434 | */
1435 |
1436 |
1437 |
1438 | /**
1439 | * Creates assertion objects representing the result of an assert call.
1440 | * Accepts an object or AssertionError as its argument.
1441 | *
1442 | * @param {object} obj
1443 | * @api public
1444 | */
1445 |
1446 | exports.assertion = function (obj) {
1447 | return {
1448 | method: obj.method || '',
1449 | message: obj.message || (obj.error && obj.error.message) || '',
1450 | error: obj.error,
1451 | passed: function () {
1452 | return !this.error;
1453 | },
1454 | failed: function () {
1455 | return Boolean(this.error);
1456 | }
1457 | };
1458 | };
1459 |
1460 | /**
1461 | * Creates an assertion list object representing a group of assertions.
1462 | * Accepts an array of assertion objects.
1463 | *
1464 | * @param {Array} arr
1465 | * @param {Number} duration
1466 | * @api public
1467 | */
1468 |
1469 | exports.assertionList = function (arr, duration) {
1470 | var that = arr || [];
1471 | that.failures = function () {
1472 | var failures = 0;
1473 | for (var i=0; i(' +
1916 | '' + assertions.failures() + ', ' +
1917 | '' + assertions.passes() + ', ' +
1918 | assertions.length +
1919 | ')';
1920 | test.className = assertions.failures() ? 'fail': 'pass';
1921 | test.appendChild(strong);
1922 |
1923 | var aList = document.createElement('ol');
1924 | aList.style.display = 'none';
1925 | test.onclick = function () {
1926 | var d = aList.style.display;
1927 | aList.style.display = (d == 'none') ? 'block': 'none';
1928 | };
1929 | for (var i=0; i' + (a.error.stack || a.error) + '';
1935 | li.className = 'fail';
1936 | }
1937 | else {
1938 | li.innerHTML = a.message || a.method || 'no message';
1939 | li.className = 'pass';
1940 | }
1941 | aList.appendChild(li);
1942 | }
1943 | test.appendChild(aList);
1944 | tests.appendChild(test);
1945 | },
1946 | done: function (assertions) {
1947 | var end = new Date().getTime();
1948 | var duration = end - start;
1949 |
1950 | var failures = assertions.failures();
1951 | banner.className = failures ? 'fail': 'pass';
1952 |
1953 | result.innerHTML = 'Tests completed in ' + duration +
1954 | ' milliseconds.
' +
1955 | assertions.passes() + ' assertions of ' +
1956 | '' + assertions.length + ' passed, ' +
1957 | assertions.failures() + ' failed.';
1958 | }
1959 | });
1960 | };
1961 | })(reporter);
1962 | nodeunit = core;
1963 | nodeunit.assert = assert;
1964 | nodeunit.reporter = reporter;
1965 | nodeunit.run = reporter.run;
1966 | return nodeunit; })();
1967 |
--------------------------------------------------------------------------------