1

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?

Since001
  • 81
  • 1
  • 8

3 Answers3

1

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);
})
Community
  • 1
  • 1
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • Thank you for your quick answer! But where would I run my exec function in this case? – Since001 Jan 10 '16 at 11:26
  • I'm kind of getting it now, thanks But unfortunately it's still not working. This is my current code in module.js `child_process = require('child_process'); exports.myFunction1 = function() { return new Promise(function(resolve){ // this function runs for like 3 seconds var result = child_process.execSync('git log', { encoding: 'utf8' }); resolve(result); }); };` And I'm using it like this in my main.js `module.myFunction1() .then(function(stdout){ console.log("Output myFunction1: " + stdout); });` But I'm not getting any output in main.js :( – Since001 Jan 10 '16 at 14:11
0

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.

madox2
  • 49,493
  • 17
  • 99
  • 99
0

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);});

Flash Thunder
  • 11,672
  • 8
  • 47
  • 91