Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async does not protect against array modifications during the function's runtime #572

Closed
bradens opened this issue Jul 4, 2014 · 6 comments
Labels

Comments

@bradens
Copy link

bradens commented Jul 4, 2014

When you have an asynchronous call eg. async.each, and during that runtime, the array which is passed in has modifications to it, then it may never finish, or will call the callback twice.

Example:

async = require "async" 
arr = [1, 2, 3]  
async.each arr, ((i, cb) -> console.log "i"; setImmediate(cb)), (err) -> console.log "done" 
arr.push(4)

This example loops through the 3 original array elements, and prints i but then never calls the callback, because in async.each it's doing:

if (completed >= arr.length) {
  callback(null);
}

When looking at the async code, it's doing a comparison against arr.length which can change...would it not be better to store the original array length and do a comparison against that, ensuring that the completed callback will be called?

Fiddle:
http://jsfiddle.net/4ysKX/1/

@bradens bradens changed the title Async does not protect against array modifications during the functions runtime Async does not protect against array modifications during the function's runtime Jul 4, 2014
@aearly
Copy link
Collaborator

aearly commented Jul 4, 2014

You're right -- so don't modify it. Clone your array before you pass it to an async function if you need to change it later.

@kenpratt
Copy link

kenpratt commented Jul 4, 2014

@aearly Yes, that's a work-around, but the underlying issue here is that it's a very easy mistake to make, and a very hard issue to debug, so IMHO it should be fixed in the core library. This has bitten me twice now on two different projects.

It would be hard to protect against any array modification without cloning the input, for example a synchronous modification in the iterator function, but protecting against asynchronous modification unrelated to the iteration should be straightforward even without clone -- really just not assuming arr.length will stay constant after spinning up the async jobs (saving the length in a variable before the iteration).

(I work with @bradens -- one of us will submit a PR with tests if that's welcome).

@aearly
Copy link
Collaborator

aearly commented Jul 6, 2014

A PR with tests would be welcome, but it's ultimately up to @caolan to merge it.

The downside to doing this is the extra overhead of the array copying. If you have a large array, or are calling async.each et al many times, it will be slower and use more memory.

@kenpratt
Copy link

kenpratt commented Jul 6, 2014

I agree that an array copy would be too much overhead. I'm suggesting that since the initial iteration of the array is synchronous, the original array length could be saved to check against later. That way, the number of callback calls will match the number of iterator calls, even if the array is changed in the meantime. This should be easy and zero overhead for async.each, but I haven't looked at the other functions yet. It would be nice if all the parallel functions had consistent behaviour in these cases.

@aearly aearly added the bug label May 19, 2015
@aearly
Copy link
Collaborator

aearly commented Jun 1, 2015

This is a duplicate of #557.

@ORESoftware
Copy link
Contributor

Did async end up disallowing array modifications after the fact? I have a use case where it would be nice to modify the original array after the fact, so as to iterate on more elements than initially.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants