Michael Wasser
Michael Wasser

Reputation: 1837

Javascript Async calls unrelated callback in parallel

When using a third party library that doesn't depend on the async library, somehow handling an error in the results callback calls an arbitrary callback inside one of the parallel tasks.

eg.

var async = require('async'),
    bloomjs = require('bloom-js');
var client = new bloomjs.Client({url: "http://localhost:3005/api"});

async.parallel([function (cb) {
  console.log('callback1');
  client.find('usgov.hhs.npi', '1558490003', function (err, results) {
    console.log('callback2');
    if (err) return cb(err);
    cb(results);
  });
}], function (err, results) {
  console.log('callback3');
  throw "hello";
  if(err) return console.dir(err);
  console.dir(results);
});

generates the output

callback1
callback2
callback3
callback2

/Users/untoldone/Source/async-demo/node_modules/async/lib/async.js:30
            if (called) throw new Error("Callback was already called.");
                              ^
Error: Callback was already called.
    at /Users/untoldone/Source/async-demo/node_modules/async/lib/async.js:30:31
    at /Users/untoldone/Source/async-demo/node_modules/async/lib/async.js:251:21
    at /Users/untoldone/Source/async-demo/node_modules/async/lib/async.js:575:34
    at /Users/untoldone/Source/async-demo/demo.js:9:21
    at /Users/untoldone/Source/async-demo/node_modules/bloom-js/src/bloom.js:117:18
    at IncomingMessage.<anonymous> (/Users/untoldone/Source/async-demo/node_modules/bloom-js/src/bloom.js:219:22)
    at IncomingMessage.EventEmitter.emit (events.js:117:20)
    at _stream_readable.js:920:16
    at process._tickCallback (node.js:415:13)

It seems the callback2 is somehow being used to handle an exception in callback3. But given bloomjs never uses the async library, I have no idea how this is possible. Thoughts?

Upvotes: 2

Views: 142

Answers (1)

Barmar
Barmar

Reputation: 782693

I suspect that inside bloomjs.Client.find, the callback code contains something like this:

try {
    callback(false, result);
} catch (e) {
    callback(e, result);
}

So when your callback throws the hello error, this gets caught by the above code, and re-calls the client.find callback. This would again call cb(), but async.js detects this loop and signals its own error.

Upvotes: 2

Related Questions