Knex.js - node process never exits, how to close it gracefully - but only when all queries are resolved?

Viewed 1447

I've just started using Knex.js in a command line scripts project using Node.js.

I've got it working, and can run queries etc. But when my script is finished (and all pending promises have been resolved), the Node process just hangs forever, until I kill it with ctrl+c.

Do I need to do something to tell Knex to disconnect from the database - but only once all queries are done?

I've tried putting this as the end of my script:

knex.destroy();

...but that will just kill the connection immediately, i.e. if there's a pending promise for a query that hasn't completed yet, it just gets killed immediately, and I get:

UnhandledPromiseRejectionWarning: Error: aborted
...
(node:18284) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). 

...I understand what the error is, it's killing the connection before one of my queries has finished. I just can't figure out how to solve the overall problem of having things finish up when there's nothing left to do.

I've been using Node and promises/async/await for a couple of years now, but I seem to regularly run into these issues with the process freezing at the end unless I write code to specifically to close database connections and things like this... which it often does too early. I've read many resources about async/promises, but I've never been able to find any useful info related issues with the Node process closing/exiting - but only once there's no unresolved promises. If anyone knows of any useful resources specifically related to this exit/freeze forever stuff, please let me know - it seems to be a problem that I run into regularly, so I'm likely misunderstanding something.

More generally, is there any way to tell if any Node promises are pending (anywhere globally in the project without manually writing tracking code for every single promise)? I've been trying to figure this out for a long time too, but never found any answers. If I could find this out, then I can manually run whatever closing functions are needed at that time, but there seems to not be a way to do it?

It's a large project, so it's not feasible to put await in front on every single function and query, or write wrapping code that does tedious things like manually counting every single created/resolved promise everywhere (which isn't possible anyway considering the promises are mostly generated in node_module), and I'm guessing that's not really something I should have to do anyway?

1 Answers

It sounds as if you just need to always return the promise. In calling code, you wouldn't arbitrarily exit the node process before the promise resolves, and the same is true of closing the database connection. So structures like this become useful:

function doWork () {
  Promise.all([
    createWombats(knex),
    cleanUpAardvarks(knex),
    optimiseLemurs(knex),
  ])
    .then(knex.destroy)
    .catch(() => {
      console.error('Something terrible happened.')
      knex.destroy()
    })
}

Provided each of the utility functions return a resolved or rejected promise, everything should happen in the correct order. Similar handling is possible with async/await and a try block, assuming you're happy to use blocking requests.

Related