Since001
Since001

Reputation: 81

Async functions in Node.js module

I'm kind of new to JavaScript/Node.js so bear with me. Also my english may not be that good.

I'm trying to write a Node.js module module.js with functions that do some long-running work. Kind of like this:

var exec = require('child_process').exec;

module.exports.myFunction1 = function(callback) {
    // this function runs for like 3 seconds
    exec('long running shell command' ,function(err,stdout,stderr) {
        callback(stdout);
    })
};

module.exports.myFunction2 = function(callback) {
    // this function runs for like 1 second
    exec('long running shell command' ,function(err,stdout,stderr) {
        callback(stdout);
    })
};

Now, I also have a main.js where I invoke these functions:

var module = require('./module.js');

var output1 = module.myFunction1();

var output2 = module.myFunction2();

My first problem is that my functions return undefined. I understand that this is because the exec function runs asynchronously and therefore the function returns before exec has finished. This is basically what I want but how can I tell my function that it should only callback when exec has finished?

I also don't want the functions to block node.js when I invoke them in my main.js. So basically, my output of the above code would be...

Output myFunction2: Output2
Output myFunction1: Output1

...because myFunction2() finishes faster than myFunction1().

I tried many, many solutions I found online but nothing seems to work properly.

Thank you very much in advance!

--- EDIT ---

Ok, I'm having a somewhat correct solution. Right now my code looks like this:

module.js

var Q = require('q');
require('shelljs/global')

module.exports = {
    myFunction1: function () {
        var deferred = Q.defer();

        var result = exec('long running command', {silent:true}).output.toString();

        if (ok) {
            deferred.resolve(result);
        }
        else {
            deferred.reject('Error');
        }

        return deferred.promise;
    },

    myFunction2: function () {
        var deferred = Q.defer();

        var result = exec('long running command', {silent:true}).output.toString();

        if (ok) {
            deferred.resolve(result);
        }
        else {
            deferred.reject('Error');
        }

        return deferred.promise;
    }
}

My main.js lloks like this now:

var module = require('./module');

module.myFunction1()
    .then(function (result) {
        console.log('Result 1: ' + result);
    })
    .fail(function (error) {
        console.log(error)
});

module.myFunction2()
    .then(function (result) {
        console.log('Result 2: ' + result);
    })
    .fail(function (error) {
        console.log(error)
});

And I get the expected output:

Result 1: Output that myFunction1() generated
Result 2: Output that myFunction2() generated

My Problem now is, that myFunction1() always logs before myFunction2(), even if myFunction2() finished first. Did I understood something wrong about Promises? Shouldn't myFunction2() return immediately after it finished?

Upvotes: 1

Views: 604

Answers (3)

Denys Séguret
Denys Séguret

Reputation: 382092

Your functions take callbacks. Those parameters are functions which are called on completion, which makes it easy to do

var exec = require('child_process').exec;
module.exports.myFunction1 = function(callback) {
    // this function runs for like 3 seconds
    exec('long running shell command' ,function(err,stdout,stderr) {
        callback(stdout);
    })
};

module.myFunction1(function(stdout){
      console.log("Output myFunction1: " + stdout);
});

Using a callback, in your case, is the simplest solution but you should be aware that there are other patterns to deal with asynchronous executions. Here's a good overview. For example, a popular solution, especially interesting when you have to chain asychronous continuations, is to use promises, which allow

var exec = require('child_process').exec;
module.exports.myFunction1 = function() {
    return new Promise(function(resolve, fail){
        // this function runs for like 3 seconds
        exec('long running shell command' ,function(err,stdout,stderr) {
            if (err) fail(err);
            else resolve(stdout, stderr);
        });
    });
};

module.myFunction1()
.then(function(stdout){
      console.log("Output myFunction1: " + stdout);
})
.then(module.myFunction2)
.then(function(stdout){
      console.log("Output myFunction2: " + stdout);
})

Upvotes: 1

Flash Thunder
Flash Thunder

Reputation: 12036

Callback functions don't return values directly... what you need is to setup what will happen when value will get read. Something like this:

my_function(what_will_happen_when_my_function_will_get_finished());

exectly:

myFunction1(function(data){console.log('hello! I've finished, and received: '+data);});

Upvotes: 0

madox2
madox2

Reputation: 51841

At first, I would suggest you to handle errors (err, stderr) in your modules. As you can see, your functions takes one argument which is callback. If your asynchronous function runs, the callback function is called. So you can use it like this:

module.myFunction1(function(stdout) {
    console.log("Output myFunction1: " + stdout);
    module.myFunction2(function(stdout2) {
        console.log("Output myFunction2: " + stdout2);
    });
});

exec function also takes callback function (with first argument error err - error first callbacks). There are other options how to handle flow control of asynchronous code (e.g. library async). You can also learn about Promises which is today's alternative to error first callbacks.

Upvotes: 0

Related Questions