async.js 19 KB

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