How to prevent Node.js from exiting while waiting for a callback?

node.js

node.js Problem Overview


I have code like this:

var client = new mysql.Client(options);
console.log('Icanhasclient');

client.connect(function (err) {
  console.log('jannn');
  active_db = client;
  console.log(err);
  console.log('hest');

  if (callback) {
    if (err) {
      callback(err, null);
    }

    callback(null, active_db);
  }
});

My problem is that Node terminates immediately when I run it. It prints 'Icanhasclient', but none of the console.log's inside the callback are called.

(mysql in this example is node-mysql.

Is there something that can be done to make node.js wait for the callback to complete before exiting?

node.js Solutions


Solution 1 - node.js

Callback is Not Queued

Node runs until all event queues are empty. A callback is added to an event queue when a call such as

  emmiter1.on('this_event',callback).

has executed. This call is part of the code written by the module developer .

If a module is a quick port from a synchronous/blocking version, this may not happen til some part of the operation has completed and all the queues might empty before that occurs, allowing node to exit silently.

This is a sneaky bug, that is one that the module developer might not run into during development, as it will occur less often in busy systems with many queues as it will be rare for all of them to be empty at the critical time.

A possible fix/bug detector for the user is to insert a special timer event before the suspect function call.

Solution 2 - node.js

You can just issue a setTimeout or a recurring timeout with setInterval.

If you want to check for exit conditions, you can also do a conditional timeout:

(function wait () {
   if (!SOME_EXIT_CONDITION) setTimeout(wait, 1000);
})();

Put this at the end of your code and the console will just wait ... and wait ... until you want it to close.

Solution 3 - node.js

My solution was to instantiate an EventEmitter, and listen for my custom event.

var eventEmitter = new process.EventEmitter();

then I called eventEmitter.emit from the async callback:

client.connect(function (err) {
    eventEmitter.emit('myevent', {something: "Bla"})
});

The last thing in my script was the eventEmitter.on:

eventEmitter.on('myevent', function(myResult){
  // I needed the result to be written to stdout so that the calling process could get it
  process.stdout.write(JSON.stringify(myResult));
});

Node will then wait until the event handler finishes running.

Solution 4 - node.js

Based on @Todd's answer, I created a one-liner. Include it in the beginning of your script, and set done = true when you are done:

var done = (function wait () { if (!done) setTimeout(wait, 1000) })();

Example:

var done = (function wait () { if (!done) setTimeout(wait, 1000) })();

someAsyncOperation().then(() => {
  console.log('Good to go!');
  done = true;
});

How does it work? If we expand it a bit:

// Initialize the variable `done` to `undefined`
// Create the function wait, which is available inside itself
// Note: `var` is hoisted but `let` is not so we need to use `var`
var done = (function wait () {

  // As long as it's nor marked as done, create a new event+queue
  if (!done) setTimeout(wait, 1000);
 
  // No return value; done will resolve to false (undefined)
})();

Solution 5 - node.js

Here is my two cents:

async function main()
{
    await new Promise(function () {});
    console.log('This text will never be printed');
}

function panic(error)
{
    console.error(error);
    process.exit(1);
}

// https://stackoverflow.com/a/46916601/1478566
main().catch(panic).finally(clearInterval.bind(null, setInterval(a=>a, 1E9)));

Solution 6 - node.js

Here's how I do it. I make my main entrypoint return a promise, then I use this little wrapper to ensure that as long as that promise isn't settled, node won't exit:

function wrapPromiseMain(entryPoint) {
    const pollTime = 1000000;
    let interval = setInterval(() => {}, pollTime);
    
    return entryPoint().finally(() => {clearInterval(interval)});
}

To use it, just promise-ize your main entry point and pass it as an argument to the wrapper:

function main() {

    // ... main stuff ...

    return doSomethingAsync();
}

wrapPromiseMain(main);

I find it a bit tidier than the basic polling loops because it will auto-cancel the timer when the promise settles, so it doesn't add any additional latency. The polling time can therefore be basically forever if you want it to.

Solution 7 - node.js

I did look at felixge/node-mysql library and didn't see a reference to the command client.connect in the API. Is this the actual call you're trying to make (not trying to be nitpicky here)? Regardless, IMHO you need to think more about how Javascript is designed, because it uses a programming paradigm different than most other popular languages.

The first issue I see in your code is that you haven't defined the callback, so it doesn't actually exist. I'd assume console.log(callback) is undefined. From your code, the anonymous function is the 'callback' for the client.connect function. You have to define what you are calling 'callback' at a higher scope. For example, I will define a function myCallback to exist in the scope higher than the client.connect's anonymous function. It may be useful to lookup Javacscript variable scope.

    var myCallback(err, response) {
      if (err) {
        console.log('err:%s',err);
      } else {
        console.log('response:%s',response);
      }
    }
    
    client.connect(err, function(response) {
      // this anonymous function is the callback to client.connect, the var
      // 'callback' would be undefined.
      if (err) {
        myCallback(err);
        return; // Explicit call to return, else the lines below would run.
      } 
      myCallback(null, response);
    });

Second, if you do not explicitly call return within Javascript, the function will continue to process. I was bitten by this myself. Finally, Javascript runs an event-driven loop meaning it will never wait for functions to return a value, which is why we have all these callbacks in the first place. You can force Javascript to behave differently, for example by using a while loop until a condition is true. See the 'async' library by caolan, for various strategies of manipulating the event loop. The major disadvantage to overusing these methods is your actually wasting CPU cycles/blocking when you probably should use more callbacks and simply re-think how your programs works.

Solution 8 - node.js

Please try this. Check if this help.

var client = new mysql.Client(options);
console.log('Icanhasclient');
var verbose;

if (!verbose) {
	return new Promise(function (resolve, reject) {
		client.connect(function (err) {
			if (err) {
				console.log(Error in connecting
				SQL ${err}
			)
				;
				return reject(err);
			}
			verbose = client;
			return resolve(verbose);
		})
	})
} else {
	return new Promise(function (resolve) {
		resolve(verbose);
	})
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmiklView Question on Stackoverflow
Solution 1 - node.jsgeorge calvertView Answer on Stackoverflow
Solution 2 - node.jsToddView Answer on Stackoverflow
Solution 3 - node.jsyou786View Answer on Stackoverflow
Solution 4 - node.jsFrancisco PresenciaView Answer on Stackoverflow
Solution 5 - node.jsvbarbaroshView Answer on Stackoverflow
Solution 6 - node.jsad1c371f7d05View Answer on Stackoverflow
Solution 7 - node.jsexshovelrydrView Answer on Stackoverflow
Solution 8 - node.jsTusharView Answer on Stackoverflow