Reputation: 7823
Is there an elegant way to asynchronously map an object or array in coffeescript? (Or javascript.)
Imagine I have some things in an object:
things =
x:
...
y:
...
z:
...
thingCount = 3
I want to create a method that will process each of these things and return the processed object. The process has to make an asynchronous call to fetch some info about each thing. At first I tried just to loop through the properties like so:
processThings = (callback) ->
processedThings = {}
count = 0
for key,val in things
asyncJob key,val (err,result) ->
if err
callback error
else
# PROBLEM: key has the incorrect value here
processedThings[key] = result
count += 1
if count == thingCount
callback null,processedThings
The problem is that the value of key changes in the loop. So my solution is to create a sub-function so that the key variable is contained within its closure:
processThings = (callback) ->
processedThings = {}
count = 0
processThing = (key,val) ->
asyncJob key,val (err,result) ->
if err
callback error
else
processedThings[key] = result
count += 1
if count == thingCount
callback null,processedThings
processThing key,val for key,val of things
But boy howdy that sure is fugly. Is there a preferred pattern for this?
Upvotes: 2
Views: 811
Reputation: 123513
CoffeeScript covers this with the do
keyword, described at the end of Loops and Comprehensions:
When using a JavaScript loop to generate functions, it's common to insert a closure wrapper in order to ensure that loop variables are closed over, and all the generated functions don't just share the final values. CoffeeScript provides the
do
keyword, which immediately invokes a passed function, forwarding any arguments.for filename in list do (filename) -> fs.readFile filename, (err, contents) -> compile filename, contents.toString()
It can be applied to your code as:
processThings = (callback) ->
processedThings = {}
count = 0
for key,val in things
# add this to close over `key`
do (key) ->
asyncJob key,val (err,result) ->
if err
callback error
else
processedThings[key] = result
count += 1
if count == thingCount
callback null,processedThings
Upvotes: 5