├── .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 |

Async.js Test Suite

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 | --------------------------------------------------------------------------------