You sound like we can be innovative on how we code but how can steps that do
- Parse inputs
- Query database
- Return output
be asynchronous?
Unless I'm writing a background job that is decoupled from HTTP requests, everything after the router is synchronous.
I see this over and over.
"Thing thing = await loadThing(); ": Start loading thing, then await so the main thread can continue doing other stuff while thing is loaded.
"Thing other = await thing.doSomething();": Invoke some other function that does some stuff in the background, then await so the main thread can continue doing other stuff while thing does something.
"thing = await saveThing();": Saving can often be done in parallel so no need to block the App while something is saved. Invoke the save function, then await so the main thread can continue doing other stuff while thing is saved.
This gets you the advantages of async programming while still maintaining a legible coding style that looks similar to common synchronous code.
As you put it, "Once you get into the controllers they tend to become entirely procedural and effectively completely synchronous" but that's because for most people it's far easier to think about a problem as a series of individual steps. Even then, if you have a bunch of serial awaits it's usually pretty trivial to go back and then await on a Promise.all() if you realize things can be run concurrently.