Any drawbacks of having all functions async?

Viewed 3616

The newer node js has async await, which are really cool because it makes the code look better.

I was wondering if it's a good idea to make every class method async, even if it doesn't need to return a promise?

In my use case I actually sort of need that because I'm trying to share dependencies between multiple child processes, and I'm using a combination of Proxy and child process communication to achieve this. And obviously I need promises because I need to wait for the processes to respond or send messages.

But does this have any potential side effects, maybe in the long term?

To be more clear, I want to do this solely for the benefit of cool syntax.

const database = CreateProxy('database');
await database.something();

From another process.

versus some code that just requests something from the parent process like

process.send('getSomethingFromDb');

Under the hood both use messaging, but the first one makes it look like it doesn't on the surface

3 Answers

This concept is a subject for Occam's razor. There are no benefits, while there may be disadvantages.

Extra overhead (both CPU and time) is one of the problems. Promises take some time and resources to be resolved. This may take much less than a millisecond, yet a lag can accumulate.

Another problem is that asynchronicity is contagious, once it reaches module scope, async IIFE should be used everywhere - because top-level await isn't supported yet:

module.export = (async () => {
  await require('foo');

  // await was accidentally dropped
  // this results in race condition and incorrect error handling
  require('foo');
  ...
})();

And here's a good example of a disadvantage that complicates error handling:

async function foo() {
  throw new Error('foo');
}

async function bar() {
  try {
    return foo();
  } catch (err) {
    console.log('caught with bar');
  }
}


bar(); // UnhandledPromiseRejectionWarning: Error: foo

Despite control flow looks synchrounous-like in async, errors are handled differently. foo returns rejected promise, and returned values aren't handled with try..catch in async functions. A rejection won't be handled in bar.

This wouldn't happen with regular function:

function foo() {
  throw new Error('foo');
}

function bar() {
  try {
    return foo();
  } catch (err) {
    console.log('caught with bar');
  }
}


bar(); // caught with bar

Things may become more complicated with third-party libraries that were designed to work synchronously.

I was wondering if it's a good idea to make every class method async, even if it doesn't need to return a promise?

Your code will work, but I would not recommand it for two reason :

  • Unnecessary memory/cpu usage

  • It will makes your code hard to understand. Knowing which function is asynchronous or synchronous is important to understand how a system work and what it is doing.

The result of calling an async function will always be a Promise, regardless of if the function implements any asynchronous behavior.

const asyncTest = async () => 3;
console.log(asyncTest()); // logs 'PromiseĀ {<resolved>: 3}'

Because of that, you have to always be sure to call such function with await. But that is purely a comfort issue, if even that for you. But also, creating and resolving Promises adds a little bit of time to each function call, so if performance is critical, you should avoid to call async functions in large numbers, if it can be avoided.

Related