Leon Smiers
Leon Smiers

Reputation: 11

nodejs callback using module.exports

I'm starting with NodeJS, and trying to understand how callbacks work in relation to module.exports.

I've got two NodeJS files.

  1. s1.js receives an input field msg and passes result back. s1.js is exposed via module.exports
  2. start.js includes s1.js (via require) and receives the result that I'd like to use in the program

start.js

var s1=require('./s1.js');
var r1;
s1.test("Leon", function(err, result) {
    if (err){console.log("Error occurred "+err);}
    console.log("Callback after s1.test execution "+result);    
    r1=result;
}   
);
console.log("result "+r1);
console.log("end");

s1.js

module.exports.test=function test(msg,result){
    result="Hello "+msg;
    console.log("Inside s1.js - "+result);
};

When I execute start.js the result is below

Inside s1.js - Hello Leon
result undefined
end

As expected, result is undefined, since the callback for s1.test is not finished yet.
What I do not understand is why the callback for s1.test is NEVER reached.

Any ideas?
Thanks Léon

Upvotes: 1

Views: 5711

Answers (3)

Matijs
Matijs

Reputation: 2553

The previous answers are correct, but your problem has little to do with nodejs or the module.exports but the problem is that you do not yet understand the nature of callbacks as used in JavaScript (and PHP and other languages).

The examples you use actually make this difference harder to understand. So here is a step by step description on how to understand callbacks, beginning with simple statements and ending in code looking like yours.

// Create some object
var fooBar = { foo: 'foo object'};

// Add a bar function to that object
fooBar.bar = function (param1, param2) {
    console.log("Inside bar function: " + param1);
};

Running this code doesn't log anything as the object is created and has a function added to it, but that function is never called.

For that we need to add a call to that function:

fooBar.bar('test 1');

Now we get the output:

Inside bar function: test 1

Or we can call the function twice:

fooBar.bar('test 1');
fooBar.bar('test 2');

Now we get the output:

Inside bar function: test 1
Inside bar function: test 2

The next step is to create a function like the one you used as the second parameter in the call to s1.test(). But instead of assigning it to that parameter we assign it to another variable.

// Create some object
var fooBar = { foo: 'foo object'};

// Add a bar function to that object
fooBar.bar = function (param1, param2) {
    console.log("Inside bar function: " + param1);
};

// Create assign a function to a variable
var func = function (err, result) {
    console.log("Inside func function, call using: " + err + " and " + result);    
}   

fooBar.bar('test 1');
fooBar.bar('test 2');

Her we assign the function to the variable func. func is just another variable and has the type function. A callback in JavaScript is just a variable with the type of function. To see this run:

console.log(typeof func);

The output is:

function

The output is not changed! Defining and assigning this new function to a variable as a callback does not run the code! You need to call the function to run the code. So we start to call the function and change the last two lines to four:

func('outside', 'call 1');
fooBar.bar('test 1');
fooBar.bar('test 2');
func('outside', 'call 2');

The output now is:

Inside func function, call using: outside and call 1
Inside bar function: test 1
Inside bar function: test 2
Inside func function, call using: outside and call 2

Now to get closer to your example we pass the func variable as the second parameter to the fooBar.bar() function:

func('outside', 'call 1');
fooBar.bar('test 1', func);
fooBar.bar('test 2', func);
func('outside', 'call 2');

The output remains:

Inside func function, call using: outside and call 1
Inside bar function: test 1
Inside bar function: test 2
Inside func function, call using: outside and call 2

Why? Because the code inside the fooBar.bar() function does not do anything with the second parameter. So we change this function to:

// Add a bar function to that object
fooBar.bar = function (param1, param2) {
    console.log("Inside bar function: " + param1);
    console.log("The type of param 2 is: " + typeof param2);
};

The resulting output is something like this::

Inside func function, call using: outside and call 1
Inside bar function: test 1
The type of param 2 is: function
Inside bar function: test 2
The type of param 2 is: function
Inside func function, call using: outside and call 2

This output shows the value of param2 is the function itself, not an output result! To run the code, all we need to do is to use param2 as the function it is, we can even call the function more than once:

// Add a bar function to that object
fooBar.bar = function (param1, param2) {
    param2("inside bar 1", param1);
    console.log("Inside bar function: " + param1);
    param2("inside bar 2", param1);
};

The result is:

Inside func function, call using: outside and call 1
Inside func function, call using: inside bar 1 and test 1
Inside bar function: test 1
Inside func function, call using: inside bar 2 and test 1
Inside func function, call using: inside bar 1 and test 2
Inside bar function: test 2
Inside func function, call using: inside bar 2 and test 2
Inside func function, call using: outside and call 2

Now it is finally time to create code that looks like your start.js. Instead of creating a variable func for the functions we use an anonymous functions like in you example. The full code becomes:

// Create some object
var fooBar = { foo: 'foo object'};

// Add a bar function to that object
fooBar.bar = function (param1, param2) {
    param2("inside bar 1", param1);
    console.log("Inside bar function: " + param1);
    param2("inside bar 2", param1);
};

// fooBar.bar('test 2', func);

// Use anonymous function instead of func
fooBar.bar('test 3', function (err, result) {
    console.log("Inside anonymous function, call using: " + err + " and " + result);    
}
);

The output now becomes:

Inside anonymous function, call using: inside bar 1 and test 3
Inside bar function: test 3
Inside anonymous function, call using: inside bar 2 and test 3

We can add another call to fooBar.bar with another function:

fooBar.bar('test 4', function (err, result) {
    console.log("Inside another anonymous function, call using: " + err + " and " + result);
}
);

The output:

Inside anonymous function, call using: inside bar 1 and test 3
Inside bar function: test 3
Inside anonymous function, call using: inside bar 2 and test 3
Inside another anonymous function, call using: inside bar 1 and test 4
Inside bar function: test 4
Inside another anonymous function, call using: inside bar 2 and test 4

I hope these examples clarify why the anonymous function in your start.js is never called.

One last note of interest: is there a difference between creating an anonymous function and assigning it to variable and just creating a function in javaScript? That is between:

// Create assign a function to a variable
var func = function (err, result) {

    console.log("Inside func function, call using: " + err + " and " + result);    
}

and:

function func (err, result) {
    console.log("Inside func function, call using: " + err + " and " + result);    
} 

The answer is no! Both create a pointer with the name func in the global stack to point to the same function. You can even create a global function with another name and have the func variable point to it. This example works as well as the previous one used:

function funcy (err, result) {
    console.log("Inside func function, call using: " + err + " and " + result);    
} 

var func = funcy;

In other words: a variable can point to a function just like a variable can point to a string, an integer or an object. A function is "just" one of the types a variable can point to.

Assigning this function to a variable does not execute the code! For that to happen you have to call the function the variable points to.

Upvotes: 5

Rahul Jain
Rahul Jain

Reputation: 3748

Please try below example for s1.js, the code is self explanatory, let me know if you still need help.

var defaultValues = module.exports

defaultValues.GetApiURL = function () {
    var apiURL = "http://localhost:1000/api/"
    return apiURL;
}

Upvotes: 0

Snekw
Snekw

Reputation: 2620

You are not calling your callback. Callbacks are just normal functions that are just given as parameter for the function.

Your code doesn't call the callback. You are trying to set a value to the callback. Fixed version of the s1.js:

module.exports.test=function test(msg,result){
    result(null, "Hello "+msg);
    console.log("Inside s1.js - "+result);
};

That way you are calling the callback. We need to pass in the null as you have defined the callback function as function(err, result). It has two parameters and the first one is err we didn't run in to errors so we just pass in null.

Upvotes: 0

Related Questions