Any drawbacks of having all functions async? [closed]
Asked Answered
G

3

17

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

Gutbucket answered 14/1, 2019 at 14:23 Comment(3)
Well it's not without a little extra overhead.Willett
Admittedly I'm basing this off of working with projects in C# as opposed to JS, but it's nice to know which methods rely on external callouts and the like because they're marked async. Can't help but feel that marking everything async muddies the waters a bit.Differential
yes what's up with the votes?Gutbucket
I
14

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.

Imprimis answered 14/1, 2019 at 14:31 Comment(4)
It's actually easier to handle errors by using a .catch clause: foo().catch((err) => {}); try/catch isa mess anyway. A more good thing with async functions is that you don't need to think about using try/catch or use .catch... just use catch on both sync and async functionsRihana
It wasn't obvious the first time I read it, but the reason it's a UnhandledPromiseRejectionWarning instead of 'caught with bar' is a missing await in return foo();. If you await it's caught by the try / catch. If you return the Promise it's an uncaught errorIntentional
@Intentional This is correct, a way to catch it is try { return await foo() }.... This illustrates that async functions cannot just be used as drop-in replacements for regular functions due to differences in their semantics.Imprimis
Update: as of TS4.7 by setting up tsconfig and package.json Top-Level await is available. But as told use it wisely. Code may be more readable but harder to understand.Mathamathe
T
7

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.

Tristich answered 14/1, 2019 at 14:32 Comment(2)
Nice job for pointing out how important readability is - its 90% of coding in my eyes!Raquel
2nd point is underrated. JavaScript is temporal spaghetti code, with the runtime providing all the hidden gotos. It is too common to think async means you "don't have to worry about when" the code is executed. It all works fine until it doesn't, usually when the code has become complex enough to make locating the temporal bug painful.Goodnatured
I
4

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.

Impolicy answered 14/1, 2019 at 14:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.