People always say that coroutines make code easier to understand, but I've always found normal asynchronous code with callbacks much easier to understand.
They're equivalent except that asynchronous callbacks is what actually happens and you have clear control and visibility on how control flow moves.
If you want to see the callbacks, an alternative middle ground is promises, e.g. code that looks like doSomething().then(() => doSomethingElse()).then(() => doLastTask());
I currently work on a project that involves Java code with a promise library, and Unreal Engine C++ code which does not and uses callbacks (and do async JS stuff in my personal projects), and both have to do asynchronous logic. The Unreal code is just so much harder to deal with.
Specific problems the Unreal code has:
- There's no "high level" part of the code that you can look at to see what the logic flow is.
- Many functions are side-effecty, triggering the next part of the sequential logic without it being clear that that's what they're doing. Like the handleFetchAccount() callback kicks off another httpRequest for the next step, but you wouldn't know that it does that just from the name.
I'd admit some of these problems might be mitigatable in a better written codebase though.
The coroutine approach shines for complex business-logic.
Consider this example algorithm, of several async steps,
1. Download a file into memory
2. Email a link to a review web page.
3. Wait for the user to review.
4. Upload the file to a partner.
5. Update a database.
You could implement this as callbacks. Callback from each step leads to the next being triggered. Downside - your business logic is spread across all the callbacks. You could mitigate this somewhat by defining a class with one method for each step, with those methods being defined in the same visual order as the algorithm. Then have each callbacks call a method. (The article shows something different but similar with its Command autoCommand pattern.)
Tricks like this only go so far. Imagine if the reviewer user had a choice of pressing 'approve' or 'reject' on the webserver interface, with the algorithm changing depending on their answer. How do you now represent the business logic so the programmer can follow it?
Such changes are easy in coroutines. Here is the algorithm with that variation in coroutine code,
You state that callback code gives you easy visibility to what actually happens - yes, they do. When you read callback code, it is natural to follow business-logic to system calls. Coroutine tends towards code of layered business-logic and abstraction.
Callbacks are no more 'what actually happens' than coroutines are; what actually happens involves a lot of jumping to memory addresses, and closure state is just as much a compiler invention as async/await. Blocking-style code, by comparison, is how we actually think about the business logic; language features that abstract over callback hell to let you write it make code inherently more clear. People always say it because it's true.
> They're equivalent except that asynchronous callbacks is what actually happens [...]
Neither stackful nor stackless coroutines work like this practice. The former suspends coroutines by saving and restoring the CPU state (and stack) and the latter compiles down to state machines, as mentioned in the article. Coroutines are functionally not equivalent to callbacks at all.
Which is exactly what happens when you use asynchronous callbacks except that you have to do the storing of state explicitly. Stackless coroutines even typically compile to (or are defined as equivalent to) callback based code.
I imagine you would get a lot of blank stares with that POV, at least from folks with working bullshit detectors (like young kids that haven’t been conditioned to “modern” industry practices).
I found the article a great example of the kind of crap that passes for programming these days.
In the context of the first robotics competition, teaching the command hierarchy is a good opportunity to teach students what a state machine is... And that can be a good opportunity to talk about what a computer does, because the computer is basically a hardware implementation of a state machine.
But that isn't the kind of lesson that you want to be cramming into the middle of the competition season.
They're equivalent except that asynchronous callbacks is what actually happens and you have clear control and visibility on how control flow moves.