async.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. /*global setTimeout: false, console: false */
  2. (function () {
  3. var async = {};
  4. // global on the server, window in the browser
  5. var root = this,
  6. previous_async = root.async;
  7. if (typeof module !== 'undefined' && module.exports) {
  8. module.exports = async;
  9. }
  10. else {
  11. root.async = async;
  12. }
  13. async.noConflict = function () {
  14. root.async = previous_async;
  15. return async;
  16. };
  17. //// cross-browser compatiblity functions ////
  18. var _forEach = function (arr, iterator) {
  19. if (arr.forEach) {
  20. return arr.forEach(iterator);
  21. }
  22. for (var i = 0; i < arr.length; i += 1) {
  23. iterator(arr[i], i, arr);
  24. }
  25. };
  26. var _map = function (arr, iterator) {
  27. if (arr.map) {
  28. return arr.map(iterator);
  29. }
  30. var results = [];
  31. _forEach(arr, function (x, i, a) {
  32. results.push(iterator(x, i, a));
  33. });
  34. return results;
  35. };
  36. var _reduce = function (arr, iterator, memo) {
  37. if (arr.reduce) {
  38. return arr.reduce(iterator, memo);
  39. }
  40. _forEach(arr, function (x, i, a) {
  41. memo = iterator(memo, x, i, a);
  42. });
  43. return memo;
  44. };
  45. var _keys = function (obj) {
  46. if (Object.keys) {
  47. return Object.keys(obj);
  48. }
  49. var keys = [];
  50. for (var k in obj) {
  51. if (obj.hasOwnProperty(k)) {
  52. keys.push(k);
  53. }
  54. }
  55. return keys;
  56. };
  57. //// exported async module functions ////
  58. //// nextTick implementation with browser-compatible fallback ////
  59. if (typeof process === 'undefined' || !(process.nextTick)) {
  60. async.nextTick = function (fn) {
  61. setTimeout(fn, 0);
  62. };
  63. }
  64. else {
  65. async.nextTick = process.nextTick;
  66. }
  67. async.forEach = function (arr, iterator, callback) {
  68. callback = callback || function () {};
  69. if (!arr.length) {
  70. return callback();
  71. }
  72. var completed = 0;
  73. _forEach(arr, function (x) {
  74. iterator(x, function (err) {
  75. if (err) {
  76. callback(err);
  77. callback = function () {};
  78. }
  79. else {
  80. completed += 1;
  81. if (completed === arr.length) {
  82. callback(null);
  83. }
  84. }
  85. });
  86. });
  87. };
  88. async.forEachSeries = function (arr, iterator, callback) {
  89. callback = callback || function () {};
  90. if (!arr.length) {
  91. return callback();
  92. }
  93. var completed = 0;
  94. var iterate = function () {
  95. iterator(arr[completed], function (err) {
  96. if (err) {
  97. callback(err);
  98. callback = function () {};
  99. }
  100. else {
  101. completed += 1;
  102. if (completed === arr.length) {
  103. callback(null);
  104. }
  105. else {
  106. iterate();
  107. }
  108. }
  109. });
  110. };
  111. iterate();
  112. };
  113. async.forEachLimit = function (arr, limit, iterator, callback) {
  114. callback = callback || function () {};
  115. if (!arr.length || limit <= 0) {
  116. return callback();
  117. }
  118. var completed = 0;
  119. var started = 0;
  120. var running = 0;
  121. (function replenish () {
  122. if (completed === arr.length) {
  123. return callback();
  124. }
  125. while (running < limit && started < arr.length) {
  126. started += 1;
  127. running += 1;
  128. iterator(arr[started - 1], function (err) {
  129. if (err) {
  130. callback(err);
  131. callback = function () {};
  132. }
  133. else {
  134. completed += 1;
  135. running -= 1;
  136. if (completed === arr.length) {
  137. callback();
  138. }
  139. else {
  140. replenish();
  141. }
  142. }
  143. });
  144. }
  145. })();
  146. };
  147. var doParallel = function (fn) {
  148. return function () {
  149. var args = Array.prototype.slice.call(arguments);
  150. return fn.apply(null, [async.forEach].concat(args));
  151. };
  152. };
  153. var doSeries = function (fn) {
  154. return function () {
  155. var args = Array.prototype.slice.call(arguments);
  156. return fn.apply(null, [async.forEachSeries].concat(args));
  157. };
  158. };
  159. var _asyncMap = function (eachfn, arr, iterator, callback) {
  160. var results = [];
  161. arr = _map(arr, function (x, i) {
  162. return {index: i, value: x};
  163. });
  164. eachfn(arr, function (x, callback) {
  165. iterator(x.value, function (err, v) {
  166. results[x.index] = v;
  167. callback(err);
  168. });
  169. }, function (err) {
  170. callback(err, results);
  171. });
  172. };
  173. async.map = doParallel(_asyncMap);
  174. async.mapSeries = doSeries(_asyncMap);
  175. // reduce only has a series version, as doing reduce in parallel won't
  176. // work in many situations.
  177. async.reduce = function (arr, memo, iterator, callback) {
  178. async.forEachSeries(arr, function (x, callback) {
  179. iterator(memo, x, function (err, v) {
  180. memo = v;
  181. callback(err);
  182. });
  183. }, function (err) {
  184. callback(err, memo);
  185. });
  186. };
  187. // inject alias
  188. async.inject = async.reduce;
  189. // foldl alias
  190. async.foldl = async.reduce;
  191. async.reduceRight = function (arr, memo, iterator, callback) {
  192. var reversed = _map(arr, function (x) {
  193. return x;
  194. }).reverse();
  195. async.reduce(reversed, memo, iterator, callback);
  196. };
  197. // foldr alias
  198. async.foldr = async.reduceRight;
  199. var _filter = function (eachfn, arr, iterator, callback) {
  200. var results = [];
  201. arr = _map(arr, function (x, i) {
  202. return {index: i, value: x};
  203. });
  204. eachfn(arr, function (x, callback) {
  205. iterator(x.value, function (v) {
  206. if (v) {
  207. results.push(x);
  208. }
  209. callback();
  210. });
  211. }, function (err) {
  212. callback(_map(results.sort(function (a, b) {
  213. return a.index - b.index;
  214. }), function (x) {
  215. return x.value;
  216. }));
  217. });
  218. };
  219. async.filter = doParallel(_filter);
  220. async.filterSeries = doSeries(_filter);
  221. // select alias
  222. async.select = async.filter;
  223. async.selectSeries = async.filterSeries;
  224. var _reject = function (eachfn, arr, iterator, callback) {
  225. var results = [];
  226. arr = _map(arr, function (x, i) {
  227. return {index: i, value: x};
  228. });
  229. eachfn(arr, function (x, callback) {
  230. iterator(x.value, function (v) {
  231. if (!v) {
  232. results.push(x);
  233. }
  234. callback();
  235. });
  236. }, function (err) {
  237. callback(_map(results.sort(function (a, b) {
  238. return a.index - b.index;
  239. }), function (x) {
  240. return x.value;
  241. }));
  242. });
  243. };
  244. async.reject = doParallel(_reject);
  245. async.rejectSeries = doSeries(_reject);
  246. var _detect = function (eachfn, arr, iterator, main_callback) {
  247. eachfn(arr, function (x, callback) {
  248. iterator(x, function (result) {
  249. if (result) {
  250. main_callback(x);
  251. main_callback = function () {};
  252. }
  253. else {
  254. callback();
  255. }
  256. });
  257. }, function (err) {
  258. main_callback();
  259. });
  260. };
  261. async.detect = doParallel(_detect);
  262. async.detectSeries = doSeries(_detect);
  263. async.some = function (arr, iterator, main_callback) {
  264. async.forEach(arr, function (x, callback) {
  265. iterator(x, function (v) {
  266. if (v) {
  267. main_callback(true);
  268. main_callback = function () {};
  269. }
  270. callback();
  271. });
  272. }, function (err) {
  273. main_callback(false);
  274. });
  275. };
  276. // any alias
  277. async.any = async.some;
  278. async.every = function (arr, iterator, main_callback) {
  279. async.forEach(arr, function (x, callback) {
  280. iterator(x, function (v) {
  281. if (!v) {
  282. main_callback(false);
  283. main_callback = function () {};
  284. }
  285. callback();
  286. });
  287. }, function (err) {
  288. main_callback(true);
  289. });
  290. };
  291. // all alias
  292. async.all = async.every;
  293. async.sortBy = function (arr, iterator, callback) {
  294. async.map(arr, function (x, callback) {
  295. iterator(x, function (err, criteria) {
  296. if (err) {
  297. callback(err);
  298. }
  299. else {
  300. callback(null, {value: x, criteria: criteria});
  301. }
  302. });
  303. }, function (err, results) {
  304. if (err) {
  305. return callback(err);
  306. }
  307. else {
  308. var fn = function (left, right) {
  309. var a = left.criteria, b = right.criteria;
  310. return a < b ? -1 : a > b ? 1 : 0;
  311. };
  312. callback(null, _map(results.sort(fn), function (x) {
  313. return x.value;
  314. }));
  315. }
  316. });
  317. };
  318. async.auto = function (tasks, callback) {
  319. callback = callback || function () {};
  320. var keys = _keys(tasks);
  321. if (!keys.length) {
  322. return callback(null);
  323. }
  324. var results = {};
  325. var listeners = [];
  326. var addListener = function (fn) {
  327. listeners.unshift(fn);
  328. };
  329. var removeListener = function (fn) {
  330. for (var i = 0; i < listeners.length; i += 1) {
  331. if (listeners[i] === fn) {
  332. listeners.splice(i, 1);
  333. return;
  334. }
  335. }
  336. };
  337. var taskComplete = function () {
  338. _forEach(listeners.slice(0), function (fn) {
  339. fn();
  340. });
  341. };
  342. addListener(function () {
  343. if (_keys(results).length === keys.length) {
  344. callback(null, results);
  345. callback = function () {};
  346. }
  347. });
  348. _forEach(keys, function (k) {
  349. var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
  350. var taskCallback = function (err) {
  351. if (err) {
  352. callback(err);
  353. // stop subsequent errors hitting callback multiple times
  354. callback = function () {};
  355. }
  356. else {
  357. var args = Array.prototype.slice.call(arguments, 1);
  358. if (args.length <= 1) {
  359. args = args[0];
  360. }
  361. results[k] = args;
  362. taskComplete();
  363. }
  364. };
  365. var requires = task.slice(0, Math.abs(task.length - 1)) || [];
  366. var ready = function () {
  367. return _reduce(requires, function (a, x) {
  368. return (a && results.hasOwnProperty(x));
  369. }, true) && !results.hasOwnProperty(k);
  370. };
  371. if (ready()) {
  372. task[task.length - 1](taskCallback, results);
  373. }
  374. else {
  375. var listener = function () {
  376. if (ready()) {
  377. removeListener(listener);
  378. task[task.length - 1](taskCallback, results);
  379. }
  380. };
  381. addListener(listener);
  382. }
  383. });
  384. };
  385. async.waterfall = function (tasks, callback) {
  386. callback = callback || function () {};
  387. if (!tasks.length) {
  388. return callback();
  389. }
  390. var wrapIterator = function (iterator) {
  391. return function (err) {
  392. if (err) {
  393. callback(err);
  394. callback = function () {};
  395. }
  396. else {
  397. var args = Array.prototype.slice.call(arguments, 1);
  398. var next = iterator.next();
  399. if (next) {
  400. args.push(wrapIterator(next));
  401. }
  402. else {
  403. args.push(callback);
  404. }
  405. async.nextTick(function () {
  406. iterator.apply(null, args);
  407. });
  408. }
  409. };
  410. };
  411. wrapIterator(async.iterator(tasks))();
  412. };
  413. async.parallel = function (tasks, callback) {
  414. callback = callback || function () {};
  415. if (tasks.constructor === Array) {
  416. async.map(tasks, function (fn, callback) {
  417. if (fn) {
  418. fn(function (err) {
  419. var args = Array.prototype.slice.call(arguments, 1);
  420. if (args.length <= 1) {
  421. args = args[0];
  422. }
  423. callback.call(null, err, args);
  424. });
  425. }
  426. }, callback);
  427. }
  428. else {
  429. var results = {};
  430. async.forEach(_keys(tasks), function (k, callback) {
  431. tasks[k](function (err) {
  432. var args = Array.prototype.slice.call(arguments, 1);
  433. if (args.length <= 1) {
  434. args = args[0];
  435. }
  436. results[k] = args;
  437. callback(err);
  438. });
  439. }, function (err) {
  440. callback(err, results);
  441. });
  442. }
  443. };
  444. async.series = function (tasks, callback) {
  445. callback = callback || function () {};
  446. if (tasks.constructor === Array) {
  447. async.mapSeries(tasks, function (fn, callback) {
  448. if (fn) {
  449. fn(function (err) {
  450. var args = Array.prototype.slice.call(arguments, 1);
  451. if (args.length <= 1) {
  452. args = args[0];
  453. }
  454. callback.call(null, err, args);
  455. });
  456. }
  457. }, callback);
  458. }
  459. else {
  460. var results = {};
  461. async.forEachSeries(_keys(tasks), function (k, callback) {
  462. tasks[k](function (err) {
  463. var args = Array.prototype.slice.call(arguments, 1);
  464. if (args.length <= 1) {
  465. args = args[0];
  466. }
  467. results[k] = args;
  468. callback(err);
  469. });
  470. }, function (err) {
  471. callback(err, results);
  472. });
  473. }
  474. };
  475. async.iterator = function (tasks) {
  476. var makeCallback = function (index) {
  477. var fn = function () {
  478. if (tasks.length) {
  479. tasks[index].apply(null, arguments);
  480. }
  481. return fn.next();
  482. };
  483. fn.next = function () {
  484. return (index < tasks.length - 1) ? makeCallback(index + 1): null;
  485. };
  486. return fn;
  487. };
  488. return makeCallback(0);
  489. };
  490. async.apply = function (fn) {
  491. var args = Array.prototype.slice.call(arguments, 1);
  492. return function () {
  493. return fn.apply(
  494. null, args.concat(Array.prototype.slice.call(arguments))
  495. );
  496. };
  497. };
  498. var _concat = function (eachfn, arr, fn, callback) {
  499. var r = [];
  500. eachfn(arr, function (x, cb) {
  501. fn(x, function (err, y) {
  502. r = r.concat(y || []);
  503. cb(err);
  504. });
  505. }, function (err) {
  506. callback(err, r);
  507. });
  508. };
  509. async.concat = doParallel(_concat);
  510. async.concatSeries = doSeries(_concat);
  511. async.whilst = function (test, iterator, callback) {
  512. if (test()) {
  513. iterator(function (err) {
  514. if (err) {
  515. return callback(err);
  516. }
  517. async.whilst(test, iterator, callback);
  518. });
  519. }
  520. else {
  521. callback();
  522. }
  523. };
  524. async.until = function (test, iterator, callback) {
  525. if (!test()) {
  526. iterator(function (err) {
  527. if (err) {
  528. return callback(err);
  529. }
  530. async.until(test, iterator, callback);
  531. });
  532. }
  533. else {
  534. callback();
  535. }
  536. };
  537. async.queue = function (worker, concurrency) {
  538. var workers = 0;
  539. var q = {
  540. tasks: [],
  541. concurrency: concurrency,
  542. saturated: null,
  543. empty: null,
  544. drain: null,
  545. push: function (data, callback) {
  546. if(data.constructor !== Array) {
  547. data = [data];
  548. }
  549. _forEach(data, function(task) {
  550. q.tasks.push({
  551. data: task,
  552. callback: typeof callback === 'function' ? callback : null
  553. });
  554. if (q.saturated && q.tasks.length == concurrency) {
  555. q.saturated();
  556. }
  557. async.nextTick(q.process);
  558. });
  559. },
  560. process: function () {
  561. if (workers < q.concurrency && q.tasks.length) {
  562. var task = q.tasks.shift();
  563. if(q.empty && q.tasks.length == 0) q.empty();
  564. workers += 1;
  565. worker(task.data, function () {
  566. workers -= 1;
  567. if (task.callback) {
  568. task.callback.apply(task, arguments);
  569. }
  570. if(q.drain && q.tasks.length + workers == 0) q.drain();
  571. q.process();
  572. });
  573. }
  574. },
  575. length: function () {
  576. return q.tasks.length;
  577. },
  578. running: function () {
  579. return workers;
  580. }
  581. };
  582. return q;
  583. };
  584. var _console_fn = function (name) {
  585. return function (fn) {
  586. var args = Array.prototype.slice.call(arguments, 1);
  587. fn.apply(null, args.concat([function (err) {
  588. var args = Array.prototype.slice.call(arguments, 1);
  589. if (typeof console !== 'undefined') {
  590. if (err) {
  591. if (console.error) {
  592. console.error(err);
  593. }
  594. }
  595. else if (console[name]) {
  596. _forEach(args, function (x) {
  597. console[name](x);
  598. });
  599. }
  600. }
  601. }]));
  602. };
  603. };
  604. async.log = _console_fn('log');
  605. async.dir = _console_fn('dir');
  606. /*async.info = _console_fn('info');
  607. async.warn = _console_fn('warn');
  608. async.error = _console_fn('error');*/
  609. async.memoize = function (fn, hasher) {
  610. var memo = {};
  611. var queues = {};
  612. hasher = hasher || function (x) {
  613. return x;
  614. };
  615. var memoized = function () {
  616. var args = Array.prototype.slice.call(arguments);
  617. var callback = args.pop();
  618. var key = hasher.apply(null, args);
  619. if (key in memo) {
  620. callback.apply(null, memo[key]);
  621. }
  622. else if (key in queues) {
  623. queues[key].push(callback);
  624. }
  625. else {
  626. queues[key] = [callback];
  627. fn.apply(null, args.concat([function () {
  628. memo[key] = arguments;
  629. var q = queues[key];
  630. delete queues[key];
  631. for (var i = 0, l = q.length; i < l; i++) {
  632. q[i].apply(null, arguments);
  633. }
  634. }]));
  635. }
  636. };
  637. memoized.unmemoized = fn;
  638. return memoized;
  639. };
  640. async.unmemoize = function (fn) {
  641. return function () {
  642. return (fn.unmemoized || fn).apply(null, arguments);
  643. };
  644. };
  645. }());