530 lines
15 KiB
JavaScript
Executable File
530 lines
15 KiB
JavaScript
Executable File
/*!
|
|
* async.js
|
|
* Copyright(c) 2010 Fabian Jakobs <fabian.jakobs@web.de>
|
|
* MIT Licensed
|
|
*/
|
|
|
|
define(function(require, exports, module) {
|
|
|
|
var STOP = exports.STOP = {}
|
|
|
|
exports.Generator = function(source) {
|
|
if (typeof source == "function")
|
|
this.source = {
|
|
next: source
|
|
}
|
|
else
|
|
this.source = source
|
|
}
|
|
|
|
;(function() {
|
|
this.next = function(callback) {
|
|
this.source.next(callback)
|
|
}
|
|
|
|
this.map = function(mapper) {
|
|
if (!mapper)
|
|
return this
|
|
|
|
mapper = makeAsync(1, mapper)
|
|
|
|
var source = this.source
|
|
this.next = function(callback) {
|
|
source.next(function(err, value) {
|
|
if (err)
|
|
callback(err)
|
|
else {
|
|
mapper(value, function(err, value) {
|
|
if (err)
|
|
callback(err)
|
|
else
|
|
callback(null, value)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.filter = function(filter) {
|
|
if (!filter)
|
|
return this
|
|
|
|
filter = makeAsync(1, filter)
|
|
|
|
var source = this.source
|
|
this.next = function(callback) {
|
|
source.next(function handler(err, value) {
|
|
if (err)
|
|
callback(err)
|
|
else {
|
|
filter(value, function(err, takeIt) {
|
|
if (err)
|
|
callback(err)
|
|
else if (takeIt)
|
|
callback(null, value)
|
|
else
|
|
source.next(handler)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.slice = function(begin, end) {
|
|
var count = -1
|
|
if (!end || end < 0)
|
|
var end = Infinity
|
|
|
|
var source = this.source
|
|
this.next = function(callback) {
|
|
source.next(function handler(err, value) {
|
|
count++
|
|
if (err)
|
|
callback(err)
|
|
else if (count >= begin && count < end)
|
|
callback(null, value)
|
|
else if (count >= end)
|
|
callback(STOP)
|
|
else
|
|
source.next(handler)
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.reduce = function(reduce, initialValue) {
|
|
reduce = makeAsync(3, reduce)
|
|
|
|
var index = 0
|
|
var done = false
|
|
var previousValue = initialValue
|
|
|
|
var source = this.source
|
|
this.next = function(callback) {
|
|
if (done)
|
|
return callback(STOP)
|
|
|
|
if (initialValue === undefined) {
|
|
source.next(function(err, currentValue) {
|
|
if (err)
|
|
return callback(err, previousValue)
|
|
|
|
previousValue = currentValue
|
|
reduceAll()
|
|
})
|
|
}
|
|
else
|
|
reduceAll()
|
|
|
|
function reduceAll() {
|
|
source.next(function handler(err, currentValue) {
|
|
if (err) {
|
|
done = true
|
|
if (err == STOP)
|
|
return callback(null, previousValue)
|
|
else
|
|
return(err)
|
|
}
|
|
reduce(previousValue, currentValue, index++, function(err, value) {
|
|
previousValue = value
|
|
source.next(handler)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.forEach =
|
|
this.each = function(fn) {
|
|
fn = makeAsync(1, fn)
|
|
|
|
var source = this.source
|
|
this.next = function(callback) {
|
|
source.next(function handler(err, value) {
|
|
if (err)
|
|
callback(err)
|
|
else {
|
|
fn(value, function(err) {
|
|
callback(err, value)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.some = function(condition) {
|
|
condition = makeAsync(1, condition)
|
|
|
|
var source = this.source
|
|
var done = false
|
|
this.next = function(callback) {
|
|
if (done)
|
|
return callback(STOP)
|
|
|
|
source.next(function handler(err, value) {
|
|
if (err)
|
|
return callback(err)
|
|
|
|
condition(value, function(err, result) {
|
|
if (err) {
|
|
done = true
|
|
if (err == STOP)
|
|
callback(null, false)
|
|
else
|
|
callback(err)
|
|
}
|
|
else if (result) {
|
|
done = true
|
|
callback(null, true)
|
|
}
|
|
else
|
|
source.next(handler)
|
|
})
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.every = function(condition) {
|
|
condition = makeAsync(1, condition)
|
|
|
|
var source = this.source
|
|
var done = false
|
|
this.next = function(callback) {
|
|
if (done)
|
|
return callback(STOP)
|
|
|
|
source.next(function handler(err, value) {
|
|
if (err)
|
|
return callback(err)
|
|
|
|
condition(value, function(err, result) {
|
|
if (err) {
|
|
done = true
|
|
if (err == STOP)
|
|
callback(null, true)
|
|
else
|
|
callback(err)
|
|
}
|
|
else if (!result) {
|
|
done = true
|
|
callback(null, false)
|
|
}
|
|
else
|
|
source.next(handler)
|
|
})
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.call = function(context) {
|
|
var source = this.source
|
|
return this.map(function(fn, next) {
|
|
fn = makeAsync(0, fn, context)
|
|
fn.call(context, function(err, value) {
|
|
next(err, value)
|
|
})
|
|
})
|
|
}
|
|
|
|
this.concat = function(generator) {
|
|
var generators = [this]
|
|
generators.push.apply(generators, arguments)
|
|
var index = 0
|
|
var source = generators[index++]
|
|
|
|
return new this.constructor(function(callback) {
|
|
source.next(function handler(err, value) {
|
|
if (err) {
|
|
if (err == STOP) {
|
|
source = generators[index++]
|
|
if (!source)
|
|
return callback(STOP)
|
|
else
|
|
return source.next(handler)
|
|
}
|
|
else
|
|
return callback(err)
|
|
}
|
|
else
|
|
return callback(null, value)
|
|
})
|
|
})
|
|
}
|
|
|
|
this.zip = function(generator) {
|
|
var generators = [this]
|
|
generators.push.apply(generators, arguments)
|
|
|
|
return new this.constructor(function(callback) {
|
|
exports.list(generators)
|
|
.map(function(gen, next) {
|
|
gen.next(next)
|
|
})
|
|
.toArray(callback)
|
|
})
|
|
}
|
|
|
|
this.expand = function(inserter, constructor) {
|
|
if (!inserter)
|
|
return this
|
|
|
|
var inserter = makeAsync(1, inserter)
|
|
var constructor = constructor || this.constructor
|
|
var source = this.source;
|
|
var spliced = null;
|
|
|
|
return new constructor(function next(callback) {
|
|
if (!spliced) {
|
|
source.next(function(err, value) {
|
|
if (err)
|
|
return callback(err)
|
|
|
|
inserter(value, function(err, toInsert) {
|
|
if (err)
|
|
return callback(err)
|
|
|
|
spliced = toInsert
|
|
next(callback)
|
|
})
|
|
|
|
})
|
|
}
|
|
else {
|
|
spliced.next(function(err, value) {
|
|
if (err == STOP) {
|
|
spliced = null
|
|
return next(callback)
|
|
}
|
|
else if (err)
|
|
return callback(err)
|
|
|
|
callback(err, value)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
this.sort = function(compare) {
|
|
var self = this
|
|
var arrGen
|
|
this.next = function(callback) {
|
|
if (arrGen)
|
|
return arrGen.next(callback)
|
|
|
|
self.toArray(function(err, arr) {
|
|
if (err)
|
|
callback(err)
|
|
else {
|
|
arrGen = exports.list(arr.sort(compare))
|
|
arrGen.next(callback)
|
|
}
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
}
|
|
|
|
this.join = function(separator) {
|
|
return this.$arrayOp(Array.prototype.join, separator !== undefined ? [separator] : null)
|
|
}
|
|
|
|
this.reverse = function() {
|
|
return this.$arrayOp(Array.prototype.reverse)
|
|
}
|
|
|
|
this.$arrayOp = function(arrayMethod, args) {
|
|
var self = this
|
|
var i = 0
|
|
this.next = function(callback) {
|
|
if (i++ > 0)
|
|
return callback(STOP)
|
|
|
|
self.toArray(function(err, arr) {
|
|
if (err)
|
|
callback(err, "")
|
|
else {
|
|
if (args)
|
|
callback(null, arrayMethod.apply(arr, args))
|
|
else
|
|
callback(null, arrayMethod.call(arr))
|
|
}
|
|
})
|
|
}
|
|
return new this.constructor(this)
|
|
|
|
}
|
|
|
|
this.end = function(breakOnError, callback) {
|
|
if (!callback) {
|
|
callback = arguments[0]
|
|
breakOnError = true
|
|
}
|
|
|
|
var source = this.source
|
|
var last
|
|
var lastError
|
|
source.next(function handler(err, value) {
|
|
if (err) {
|
|
if (err == STOP)
|
|
callback && callback(lastError, last)
|
|
else if (!breakOnError) {
|
|
lastError = err
|
|
source.next(handler)
|
|
}
|
|
else
|
|
callback && callback(err, value)
|
|
}
|
|
else {
|
|
last = value
|
|
source.next(handler)
|
|
}
|
|
})
|
|
}
|
|
|
|
this.toArray = function(breakOnError, callback) {
|
|
if (!callback) {
|
|
callback = arguments[0]
|
|
breakOnError = true
|
|
}
|
|
|
|
var values = []
|
|
var errors = []
|
|
var source = this.source
|
|
|
|
source.next(function handler(err, value) {
|
|
if (err) {
|
|
if (err == STOP) {
|
|
if (breakOnError)
|
|
return callback(null, values)
|
|
else {
|
|
errors.length = values.length
|
|
return callback(errors, values)
|
|
}
|
|
}
|
|
else {
|
|
if (breakOnError)
|
|
return callback(err)
|
|
else
|
|
errors[values.length] = err
|
|
}
|
|
}
|
|
|
|
values.push(value)
|
|
source.next(handler)
|
|
})
|
|
}
|
|
|
|
}).call(exports.Generator.prototype)
|
|
|
|
var makeAsync = exports.makeAsync = function(args, fn, context) {
|
|
if (fn.length > args)
|
|
return fn
|
|
else {
|
|
return function() {
|
|
var value
|
|
var next = arguments[args]
|
|
try {
|
|
value = fn.apply(context || this, arguments)
|
|
} catch(e) {
|
|
return next(e)
|
|
}
|
|
next(null, value)
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.list = function(arr, construct) {
|
|
var construct = construct || exports.Generator
|
|
var i = 0
|
|
var len = arr.length
|
|
|
|
return new construct(function(callback) {
|
|
if (i < len)
|
|
callback(null, arr[i++])
|
|
else
|
|
callback(STOP)
|
|
})
|
|
}
|
|
|
|
exports.values = function(map, construct) {
|
|
var values = []
|
|
for (var key in map)
|
|
values.push(map[key])
|
|
|
|
return exports.list(values, construct)
|
|
}
|
|
|
|
exports.keys = function(map, construct) {
|
|
var keys = []
|
|
for (var key in map)
|
|
keys.push(key)
|
|
|
|
return exports.list(keys, construct)
|
|
}
|
|
|
|
/*
|
|
* range([start,] stop[, step]) -> generator of integers
|
|
*
|
|
* Return a generator containing an arithmetic progression of integers.
|
|
* range(i, j) returns [i, i+1, i+2, ..., j-1] start (!) defaults to 0.
|
|
* When step is given, it specifies the increment (or decrement).
|
|
*/
|
|
exports.range = function(start, stop, step, construct) {
|
|
var construct = construct || exports.Generator
|
|
start = start || 0
|
|
step = step || 1
|
|
|
|
if (stop === undefined || stop === null)
|
|
stop = step > 0 ? Infinity : -Infinity
|
|
|
|
var value = start
|
|
|
|
return new construct(function(callback) {
|
|
if (step > 0 && value >= stop || step < 0 && value <= stop)
|
|
callback(STOP)
|
|
else {
|
|
var current = value
|
|
value += step
|
|
callback(null, current)
|
|
}
|
|
})
|
|
}
|
|
|
|
exports.concat = function(first, varargs) {
|
|
if (arguments.length > 1)
|
|
return first.concat.apply(first, Array.prototype.slice.call(arguments, 1))
|
|
else
|
|
return first
|
|
}
|
|
|
|
exports.zip = function(first, varargs) {
|
|
if (arguments.length > 1)
|
|
return first.zip.apply(first, Array.prototype.slice.call(arguments, 1))
|
|
else
|
|
return first.map(function(item, next) {
|
|
next(null, [item])
|
|
})
|
|
}
|
|
|
|
|
|
exports.plugin = function(members, constructors) {
|
|
if (members) {
|
|
for (var key in members) {
|
|
exports.Generator.prototype[key] = members[key]
|
|
}
|
|
}
|
|
|
|
if (constructors) {
|
|
for (var key in constructors) {
|
|
exports[key] = constructors[key]
|
|
}
|
|
}
|
|
}
|
|
|
|
})
|