Andre
Andre

Reputation: 13

TypeError: Callback is not a function with asyncJS in nodeJS

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

Answers (2)

Paul Midgen
Paul Midgen

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

smaili
smaili

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

Related Questions