Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a node.js function that waits for an event to fire before 'returning'?

I have a node application that is not a web application - it completes a series of asynchronous tasks before returning 1. Immediately before returning, the results of the program are printed to the console.

How do I make sure all the asynchronous work is completed before returning? I was able to achieve something similar to this in a web application by making sure all tasks we completed before calling res.end(), but I haven't any equivalent for a final 'event' to call before letting a script return.

See below for my (broken) function currently, attempting to wait until callStack is empty. I just discovered that this is a kind of nonsensical approach because node waits for processHub to complete before entering any of the asynchronous functions called in processObjWithRef.

function processHub(hubFileContents){
    var callStack = [];
    var myNewObj = {};
    processObjWithRef(samplePayload, myNewObj, callStack);
    while(callStack.length>0){
        //do nothing
    }
    return 1
}

Note: I have tried many times previously to achieve this kind of behavior with libraries like async (see my related question at How can I make this call to request in nodejs synchronous?) so please take the answer and comments there into account before suggesting any answers based on 'just use asynch'.

like image 537
Trindaz Avatar asked Mar 30 '12 01:03

Trindaz


People also ask

How do I delay a NodeJS function?

One way to delay execution of a function in NodeJS is to use the seTimeout() function. Just put the code you want to delay in the callback.

How do you wait for a promise to resolve before returning?

You can use the async/await syntax or call the . then() method on a promise to wait for it to resolve. Inside of functions marked with the async keyword, you can use await to wait for the promises to resolve before continuing to the next line of the function.


2 Answers

You cannot wait for an asynchronous event before returning--that's the definition of asynchronous! Trying to force Node into this programming style will only cause you pain. A naive example would be to check periodically to see if callstack is empty.

var callstack = [...];

function processHub(contents) {
  doSomethingAsync(..., callstack);
}

// check every second to see if callstack is empty
var interval = setInterval(function() {
  if (callstack.length == 0) {
    clearInterval(interval);
    doSomething()
  }
}, 1000);

Instead, the usual way to do async stuff in Node is to implement a callback to your function.

function processHub(hubFileContents, callback){
  var callStack = [];
  var myNewObj = {};
  processObjWithRef(samplePayload, myNewObj, callStack, function() {
    if (callStack.length == 0) {
      callback(some_results);
    }
  });
}

If you really want to return something, check out promises; they are guaranteed to emit an event either immediately or at some point in the future when they are resolved.

function processHub(hubFileContents){
  var callStack = [];
  var myNewObj = {};
  var promise = new Promise();

  // assuming processObjWithRef takes a callback
  processObjWithRef(samplePayload, myNewObj, callStack, function() {
    if (callStack.length == 0) {
      promise.resolve(some_results);
    }
  });

  return promise;
}

processHubPromise = processHub(...);
processHubPromise.then(function(result) {
  // do something with 'result' when complete
});
like image 78
Michelle Tilley Avatar answered Sep 19 '22 13:09

Michelle Tilley


The problem is with your design of the function. You want to return a synchronous result from a list of tasks that are executed asynchronously.

You should implement your function with an extra parameter that will be the callback where you would put the result (in this case, 1) for some consumer to do something with it.

Also you need to have a callback parameter in your inner function, otherwise you won't know when it ends. If this last thing is not possible, then you should do some kind of polling (using setInterval perhaps) to test when the callStack array is populated.

Remember, in Javascript you should never ever do a busy wait. That will lock your program entirely as it runs on a single process.

like image 23
Leonardo Garcia Crespo Avatar answered Sep 18 '22 13:09

Leonardo Garcia Crespo