Unfortunately, CoffeeScript won't help you that much with these kinds of problems involving asynchronous code (apart from having a more terse function syntax, which is quite nice actually).
Your code works, but with one small caveat: the order of the values in the results
array does not match with the order of their sources in the sources
. Here's a small working snippet that uses setTimeout
and a random number to get the desired unpredictable asynchronous behaviour:
calculateValues = (sources, callback) ->
counter = 0
length = sources.length
result = []
for source in sources
source.readValue (value) ->
result.push value
counter++
callback result if counter is length
sources = for i in [1..5]
do (i) ->
readValue: (cb) ->
setTimeout (-> cb "Value #{i}"), Math.random() * 1000
calculateValues sources, (results) ->
console.log "results", results
Sample output:
results ["Value 2", "Value 5", "Value 1", "Value 3", "Value 4"]
If you wanted to preserve the ordering, it's a pretty simple fix, but this showcases how tricky it can be to get async code right.
Fortunately, there are better ways to express this kind of construct with the help of other tools. I'd recommend using a simple library like Async.js to help with synchronizing things. Here's the same snippet rewritten to use async.parallel
and Node's convention of using two-argument callbacks of the form function(err, value)
(and also a bit of Underscore.js little help, which you can translate to native CS very easily if you don't want it):
calculateValues = (sources, callback) ->
funcs = _.pluck sources, 'readValue'
async.parallel funcs, callback
sources = for i in [1..5]
do (i) ->
readValue: (cb) ->
setTimeout (-> cb null, "Value #{i}"), Math.random() * 1000
calculateValues sources, (err, results) ->
console.log "results", results
Output:
results ["Value 1", "Value 2", "Value 3", "Value 4", "Value 5"]