async.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*!
  2. * async.js
  3. * Copyright(c) 2010 Fabian Jakobs <fabian.jakobs@web.de>
  4. * MIT Licensed
  5. */
  6. define(function(require, exports, module) {
  7. var STOP = exports.STOP = {}
  8. exports.Generator = function(source) {
  9. if (typeof source == "function")
  10. this.source = {
  11. next: source
  12. }
  13. else
  14. this.source = source
  15. }
  16. ;(function() {
  17. this.next = function(callback) {
  18. this.source.next(callback)
  19. }
  20. this.map = function(mapper) {
  21. if (!mapper)
  22. return this
  23. mapper = makeAsync(1, mapper)
  24. var source = this.source
  25. this.next = function(callback) {
  26. source.next(function(err, value) {
  27. if (err)
  28. callback(err)
  29. else {
  30. mapper(value, function(err, value) {
  31. if (err)
  32. callback(err)
  33. else
  34. callback(null, value)
  35. })
  36. }
  37. })
  38. }
  39. return new this.constructor(this)
  40. }
  41. this.filter = function(filter) {
  42. if (!filter)
  43. return this
  44. filter = makeAsync(1, filter)
  45. var source = this.source
  46. this.next = function(callback) {
  47. source.next(function handler(err, value) {
  48. if (err)
  49. callback(err)
  50. else {
  51. filter(value, function(err, takeIt) {
  52. if (err)
  53. callback(err)
  54. else if (takeIt)
  55. callback(null, value)
  56. else
  57. source.next(handler)
  58. })
  59. }
  60. })
  61. }
  62. return new this.constructor(this)
  63. }
  64. this.slice = function(begin, end) {
  65. var count = -1
  66. if (!end || end < 0)
  67. var end = Infinity
  68. var source = this.source
  69. this.next = function(callback) {
  70. source.next(function handler(err, value) {
  71. count++
  72. if (err)
  73. callback(err)
  74. else if (count >= begin && count < end)
  75. callback(null, value)
  76. else if (count >= end)
  77. callback(STOP)
  78. else
  79. source.next(handler)
  80. })
  81. }
  82. return new this.constructor(this)
  83. }
  84. this.reduce = function(reduce, initialValue) {
  85. reduce = makeAsync(3, reduce)
  86. var index = 0
  87. var done = false
  88. var previousValue = initialValue
  89. var source = this.source
  90. this.next = function(callback) {
  91. if (done)
  92. return callback(STOP)
  93. if (initialValue === undefined) {
  94. source.next(function(err, currentValue) {
  95. if (err)
  96. return callback(err, previousValue)
  97. previousValue = currentValue
  98. reduceAll()
  99. })
  100. }
  101. else
  102. reduceAll()
  103. function reduceAll() {
  104. source.next(function handler(err, currentValue) {
  105. if (err) {
  106. done = true
  107. if (err == STOP)
  108. return callback(null, previousValue)
  109. else
  110. return(err)
  111. }
  112. reduce(previousValue, currentValue, index++, function(err, value) {
  113. previousValue = value
  114. source.next(handler)
  115. })
  116. })
  117. }
  118. }
  119. return new this.constructor(this)
  120. }
  121. this.forEach =
  122. this.each = function(fn) {
  123. fn = makeAsync(1, fn)
  124. var source = this.source
  125. this.next = function(callback) {
  126. source.next(function handler(err, value) {
  127. if (err)
  128. callback(err)
  129. else {
  130. fn(value, function(err) {
  131. callback(err, value)
  132. })
  133. }
  134. })
  135. }
  136. return new this.constructor(this)
  137. }
  138. this.some = function(condition) {
  139. condition = makeAsync(1, condition)
  140. var source = this.source
  141. var done = false
  142. this.next = function(callback) {
  143. if (done)
  144. return callback(STOP)
  145. source.next(function handler(err, value) {
  146. if (err)
  147. return callback(err)
  148. condition(value, function(err, result) {
  149. if (err) {
  150. done = true
  151. if (err == STOP)
  152. callback(null, false)
  153. else
  154. callback(err)
  155. }
  156. else if (result) {
  157. done = true
  158. callback(null, true)
  159. }
  160. else
  161. source.next(handler)
  162. })
  163. })
  164. }
  165. return new this.constructor(this)
  166. }
  167. this.every = function(condition) {
  168. condition = makeAsync(1, condition)
  169. var source = this.source
  170. var done = false
  171. this.next = function(callback) {
  172. if (done)
  173. return callback(STOP)
  174. source.next(function handler(err, value) {
  175. if (err)
  176. return callback(err)
  177. condition(value, function(err, result) {
  178. if (err) {
  179. done = true
  180. if (err == STOP)
  181. callback(null, true)
  182. else
  183. callback(err)
  184. }
  185. else if (!result) {
  186. done = true
  187. callback(null, false)
  188. }
  189. else
  190. source.next(handler)
  191. })
  192. })
  193. }
  194. return new this.constructor(this)
  195. }
  196. this.call = function(context) {
  197. var source = this.source
  198. return this.map(function(fn, next) {
  199. fn = makeAsync(0, fn, context)
  200. fn.call(context, function(err, value) {
  201. next(err, value)
  202. })
  203. })
  204. }
  205. this.concat = function(generator) {
  206. var generators = [this]
  207. generators.push.apply(generators, arguments)
  208. var index = 0
  209. var source = generators[index++]
  210. return new this.constructor(function(callback) {
  211. source.next(function handler(err, value) {
  212. if (err) {
  213. if (err == STOP) {
  214. source = generators[index++]
  215. if (!source)
  216. return callback(STOP)
  217. else
  218. return source.next(handler)
  219. }
  220. else
  221. return callback(err)
  222. }
  223. else
  224. return callback(null, value)
  225. })
  226. })
  227. }
  228. this.zip = function(generator) {
  229. var generators = [this]
  230. generators.push.apply(generators, arguments)
  231. return new this.constructor(function(callback) {
  232. exports.list(generators)
  233. .map(function(gen, next) {
  234. gen.next(next)
  235. })
  236. .toArray(callback)
  237. })
  238. }
  239. this.expand = function(inserter, constructor) {
  240. if (!inserter)
  241. return this
  242. var inserter = makeAsync(1, inserter)
  243. var constructor = constructor || this.constructor
  244. var source = this.source;
  245. var spliced = null;
  246. return new constructor(function next(callback) {
  247. if (!spliced) {
  248. source.next(function(err, value) {
  249. if (err)
  250. return callback(err)
  251. inserter(value, function(err, toInsert) {
  252. if (err)
  253. return callback(err)
  254. spliced = toInsert
  255. next(callback)
  256. })
  257. })
  258. }
  259. else {
  260. spliced.next(function(err, value) {
  261. if (err == STOP) {
  262. spliced = null
  263. return next(callback)
  264. }
  265. else if (err)
  266. return callback(err)
  267. callback(err, value)
  268. })
  269. }
  270. })
  271. }
  272. this.sort = function(compare) {
  273. var self = this
  274. var arrGen
  275. this.next = function(callback) {
  276. if (arrGen)
  277. return arrGen.next(callback)
  278. self.toArray(function(err, arr) {
  279. if (err)
  280. callback(err)
  281. else {
  282. arrGen = exports.list(arr.sort(compare))
  283. arrGen.next(callback)
  284. }
  285. })
  286. }
  287. return new this.constructor(this)
  288. }
  289. this.join = function(separator) {
  290. return this.$arrayOp(Array.prototype.join, separator !== undefined ? [separator] : null)
  291. }
  292. this.reverse = function() {
  293. return this.$arrayOp(Array.prototype.reverse)
  294. }
  295. this.$arrayOp = function(arrayMethod, args) {
  296. var self = this
  297. var i = 0
  298. this.next = function(callback) {
  299. if (i++ > 0)
  300. return callback(STOP)
  301. self.toArray(function(err, arr) {
  302. if (err)
  303. callback(err, "")
  304. else {
  305. if (args)
  306. callback(null, arrayMethod.apply(arr, args))
  307. else
  308. callback(null, arrayMethod.call(arr))
  309. }
  310. })
  311. }
  312. return new this.constructor(this)
  313. }
  314. this.end = function(breakOnError, callback) {
  315. if (!callback) {
  316. callback = arguments[0]
  317. breakOnError = true
  318. }
  319. var source = this.source
  320. var last
  321. var lastError
  322. source.next(function handler(err, value) {
  323. if (err) {
  324. if (err == STOP)
  325. callback && callback(lastError, last)
  326. else if (!breakOnError) {
  327. lastError = err
  328. source.next(handler)
  329. }
  330. else
  331. callback && callback(err, value)
  332. }
  333. else {
  334. last = value
  335. source.next(handler)
  336. }
  337. })
  338. }
  339. this.toArray = function(breakOnError, callback) {
  340. if (!callback) {
  341. callback = arguments[0]
  342. breakOnError = true
  343. }
  344. var values = []
  345. var errors = []
  346. var source = this.source
  347. source.next(function handler(err, value) {
  348. if (err) {
  349. if (err == STOP) {
  350. if (breakOnError)
  351. return callback(null, values)
  352. else {
  353. errors.length = values.length
  354. return callback(errors, values)
  355. }
  356. }
  357. else {
  358. if (breakOnError)
  359. return callback(err)
  360. else
  361. errors[values.length] = err
  362. }
  363. }
  364. values.push(value)
  365. source.next(handler)
  366. })
  367. }
  368. }).call(exports.Generator.prototype)
  369. var makeAsync = exports.makeAsync = function(args, fn, context) {
  370. if (fn.length > args)
  371. return fn
  372. else {
  373. return function() {
  374. var value
  375. var next = arguments[args]
  376. try {
  377. value = fn.apply(context || this, arguments)
  378. } catch(e) {
  379. return next(e)
  380. }
  381. next(null, value)
  382. }
  383. }
  384. }
  385. exports.list = function(arr, construct) {
  386. var construct = construct || exports.Generator
  387. var i = 0
  388. var len = arr.length
  389. return new construct(function(callback) {
  390. if (i < len)
  391. callback(null, arr[i++])
  392. else
  393. callback(STOP)
  394. })
  395. }
  396. exports.values = function(map, construct) {
  397. var values = []
  398. for (var key in map)
  399. values.push(map[key])
  400. return exports.list(values, construct)
  401. }
  402. exports.keys = function(map, construct) {
  403. var keys = []
  404. for (var key in map)
  405. keys.push(key)
  406. return exports.list(keys, construct)
  407. }
  408. /*
  409. * range([start,] stop[, step]) -> generator of integers
  410. *
  411. * Return a generator containing an arithmetic progression of integers.
  412. * range(i, j) returns [i, i+1, i+2, ..., j-1] start (!) defaults to 0.
  413. * When step is given, it specifies the increment (or decrement).
  414. */
  415. exports.range = function(start, stop, step, construct) {
  416. var construct = construct || exports.Generator
  417. start = start || 0
  418. step = step || 1
  419. if (stop === undefined || stop === null)
  420. stop = step > 0 ? Infinity : -Infinity
  421. var value = start
  422. return new construct(function(callback) {
  423. if (step > 0 && value >= stop || step < 0 && value <= stop)
  424. callback(STOP)
  425. else {
  426. var current = value
  427. value += step
  428. callback(null, current)
  429. }
  430. })
  431. }
  432. exports.concat = function(first, varargs) {
  433. if (arguments.length > 1)
  434. return first.concat.apply(first, Array.prototype.slice.call(arguments, 1))
  435. else
  436. return first
  437. }
  438. exports.zip = function(first, varargs) {
  439. if (arguments.length > 1)
  440. return first.zip.apply(first, Array.prototype.slice.call(arguments, 1))
  441. else
  442. return first.map(function(item, next) {
  443. next(null, [item])
  444. })
  445. }
  446. exports.plugin = function(members, constructors) {
  447. if (members) {
  448. for (var key in members) {
  449. exports.Generator.prototype[key] = members[key]
  450. }
  451. }
  452. if (constructors) {
  453. for (var key in constructors) {
  454. exports[key] = constructors[key]
  455. }
  456. }
  457. }
  458. })