Reputation: 13
I want to write a function which calls itself with multiple synchronous calls (the code here is just a stripped example of the flow). The problem is that nodeJS is giving me a "TypeError: asyncCb is not a function".
I researched this error and it seems that the parameter is returning something else than a function, but I cannot find the error in the code. I'm a new nodeJS developer so I might miss something obvious here...
Thank you for your help!
var async = require('async');
//number is an integer, deepdive defines if the function is called the first time or not, cb contains the regular callback, asynCb contains the asyncJS callback
var asyncTester = function(number, deepdive, cb, asyncCb) {
//check if function should nest itself -- only on the first call
if (deepdive == true) {
var funcArray = [];
//load async with multiple calls of this function
for (var times = 2; times < 4; times++) {
funcArray.push(function(callback) {
asyncTester(times, false, null, callback);
});
}
//call async with array of functions and final callback handling
async.series(funcArray,
function(err, results) {
//return the original callback with the results from the async series
return cb(err, results);
});
}
//return the async callback when in a nested call
return asyncCb(null, number);
};
asyncTester(1, true, function(err, data) {
//expect array of results with 1, 2, 3
console.log(data);
}, null);
Upvotes: 1
Views: 8815
Reputation: 86
Thanks for the stripped-down repro. The error is being thrown because you're invoking a null-valued variable as a function.
When you call asyncTester
yourself you provide null
for asyncCb
, while the function you give to async.series
supplies an actual callback function.
I've updated your code and added a few console.log
statements to illustrate the point.
var async = require('async');
// number is an integer, deepdive defines if the function is called the
// first time or not, cb contains the regular callback, asynCb contains
// the asyncJS callback
var asyncTester = function(number, deepdive, cb, asyncCb) {
console.log(asyncCb); // <=== new
//check if function should nest itself -- only on the first call
if (deepdive == true) {
var funcArray = [];
//load async with multiple calls of this function
for (var times = 0; times < 4; times++) {
funcArray.push(function(callback) {
asyncTester(times, false, null, callback);
});
}
//call async with array of functions and final callback handling
async.series(funcArray,
function(err, results) {
//return the original callback with the results from
// the async series
console.log('.series callback'); // <=== new
return cb(err, results);
});
}
//return the async callback when in a nested call
return asyncCb(null, number);
};
asyncTester(
1, true,
function(err, data) { console.log(data); },
function() { console.log('all done!'); } // <=== new
);
Output of original with console.log:
null
[Function]
[Function]
[Function]
[Function]
.series callback
[ 4, 4, 4, 4 ]
/Users/pmidge/workspace/personal/jstest/main.js:2079
return asyncCb(null, number);
^
TypeError: asyncCb is not a function
at asyncTester (/Users/pmidge/workspace/personal/jstest/main.js:2079:16)
at test63 (/Users/pmidge/workspace/personal/jstest/main.js:2082:5)
at Object.<anonymous> (/Users/pmidge/workspace/personal/jstest/main.js:2090:1)
at Module._compile (module.js:413:34)
at Object.Module._extensions..js (module.js:422:10)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Function.Module.runMain (module.js:447:10)
at startup (node.js:139:18)
at node.js:999:3
Output with function instead of null:
[Function]
[Function]
[Function]
[Function]
[Function]
.series callback
[ 4, 4, 4, 4 ]
initiating call done!
I suspect your investigation of this issue was hampered by the fact that everything happens in order since (not sure if this was deliberate) you don't actually do anything asynchronous in this example.
We can force asynchronous execution of your inner code by substituting the following code for the body of the anonymous function you push into the array given to async.series
:
setImmediate(function() {
asyncTester(times, false, null, callback);
});
Then the console output looks like this, and it's more obvious that you have a bug in the code that runs immediately instead of in a future iteration of the event loop:
null
/Users/pmidge/workspace/personal/jstest/main.js:2079
return asyncCb(null, number);
^
TypeError: asyncCb is not a function
at asyncTester (/Users/pmidge/workspace/personal/jstest/main.js:2079:16)
at test63 (/Users/pmidge/workspace/personal/jstest/main.js:2082:5)
at Object.<anonymous> (/Users/pmidge/workspace/personal/jstest/main.js:2090:1)
at Module._compile (module.js:413:34)
at Object.Module._extensions..js (module.js:422:10)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Function.Module.runMain (module.js:447:10)
at startup (node.js:139:18)
at node.js:999:3
If you don't want to provide a callback at all, you can just guard your return statement like this:
//return the async callback when in a nested call
return asyncCb
? asyncCb(null, number)
: null;
Upvotes: 1
Reputation: 1235
Your asyncTester
function expects 4 arguments:
var asyncTester = function(number, deepdive, cb, asyncCb) {
but, you're only calling it with three:
asyncTester(1, true, function(err, data) {
so the fourth argument, asyncCb
is undefined
.
Upvotes: 0