Why does the node.js' async behave strange?

Problem

In the example:

async.eachLimit([1,2,3,4,5,6,7,8,9,10], 6, function(a, b) {
    console.log("current: " + a);
    b("done");
  },
  function(err) {
    if (err) {
      console.log("error");
    } else { 
      console.log("completely done");
    }
  }
);

Output:

current: 1
error
current: 2
current: 3
current: 4
current: 5
current: 6
undefined

Why such a strange behaviour? Where comes the error and undefined from? And where are the other 4 elements processed? When is the async's callback is called? As expected from me:

current: 1
done
current: 2
done
current: 3
done
current: 4
done
current: 5
done
current: 6
done
current: 7
done
current: 8
done
current: 9
done
current: 10
done
compeletely done

So that only 6 elements are active at the same time.

What should I change to get the behaviour of async as I expected?

Update:

and if I use

async.**eachSeries**([1,2,3,4,5,6,7,8,9,10], function(a, b) {
    console.log("current: " + a);
    b("done");
  },
  function(err) {
    if (err) {
      console.log("error");
    } else { 
      console.log("completely done");
    }
  }
);

then the output is weird too:

current: 1
error
undefined
Problem courtesy of: static

Solution

A lot of the issues stem from this line:

b("done");

This sets "done" as an err since that's what the 1st arguments is:

The iterator is passed a callback(err) which must be called once it has completed. If no error has occured, the callback should be run without arguments or with an explicit null argument.

Based on the output you listed as expected, you probably wanted:

console.log("done");
b(null); // or: `b()`

  1. Where comes the error and undefined from?

    The error is from the callback:

    if (err) {
        console.log("error");
    }
    

    The undefined, however, isn't from the snippet. Though, if you're using Node's interactive REPL, then that would be the source as it'll display eachLimit's return value (which is undefined).

  2. When is the async's callback is called?

    When an err is passed, the callback is called early.

  3. And where are the other 4 elements processed?

    Once an err has been passed, async functions will stop iterating. eachLimit only makes it to 6 because it chooses to finish the current "batch" before stopping.


As for eachLimit() vs eachSeries(), that depends on what the iterator does. Currently, the iterator is entirely synchronous, so both functions will perform rather similarly -- iterating 1 value at a time.

And, just to be clear: The async library can't make synchronous tasks asynchronous. It actually assumes the tasks are already asynchronous and simply helps to manage a group of them.

Solution courtesy of: Jonathan Lonowski

Discussion

There is currently no discussion for this recipe.

This recipe can be found in it's original form on Stack Over Flow.