But then would it make sense to make every function async?
No, that doesn't make sense at all! You're being too reductionist...
I/O has its place in every real world program, and the true limitation (or what you call a "problem") are the single-threaded runtimes of JavaScript. It's not a question of whether you should mark your functions async or not, as if it's an issue of consistency in the call-tree of your program. The true question should rather be whether your functions are I/O-bound (and would actually block the single-threaded event loop) or are solely compute-bound.
You're forgetting the fact that async/await in JavaScript was a historical design choice to prevent the callback-hell that came with single-threaded concurrency. So if you'd want to get back to that callback-hell (and convert async functions back to "some-form-of" sync), you still can[0]:
// some dummy async function that doesn't really do any I/O
async function add(
a: number,
b: number,
): Promise<number> {
return a + b;
}
// convert async function back to sync to enjoy callback-hell again
function addUnpromisified(
a: number,
b: number,
cb: ((result: number | null, reason: any) => any),
): void {
add(a, b)
.then((result) => { cb(result, null); })
.catch((reason) => { cb(null, reason); });
}
You can think of async/await as an evolution of generators[1], as every await yields control back to the event loop. I'd actually encourage you to write some of your programs' behavior with generators if you've never done that before. Generator functions will yield control back to the caller, which is an interesting way to design programs when the caller is you instead of the event loop.
It's in Python, but David Beazley still has one of the best explanations on that topic, and shows the how and why you'd want to design an event loop live on stage[2].
Comments
No, that doesn't make sense at all! You're being too reductionist...
I/O has its place in every real world program, and the true limitation (or what you call a "problem") are the single-threaded runtimes of JavaScript. It's not a question of whether you should mark your functions async or not, as if it's an issue of consistency in the call-tree of your program. The true question should rather be whether your functions are I/O-bound (and would actually block the single-threaded event loop) or are solely compute-bound.
You're forgetting the fact that async/await in JavaScript was a historical design choice to prevent the callback-hell that came with single-threaded concurrency. So if you'd want to get back to that callback-hell (and convert async functions back to "some-form-of" sync), you still can[0]:
You can think of async/await as an evolution of generators[1], as every await yields control back to the event loop. I'd actually encourage you to write some of your programs' behavior with generators if you've never done that before. Generator functions will yield control back to the caller, which is an interesting way to design programs when the caller is you instead of the event loop.It's in Python, but David Beazley still has one of the best explanations on that topic, and shows the how and why you'd want to design an event loop live on stage[2].
[0]: https://www.typescriptlang.org/play/?#code/PTAEGcHsFsFNQCYFd...
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guid...
[2]: https://www.youtube.com/watch?v=MCs5OvhV9S4