With callbacks you have to make sure that your data persists across the function calls. This necessarily requires more heap allocations (or copies) than in a coroutine where most data can just live on the stack.
With callbacks you have to make sure that your data persists across the function calls. This necessarily requires more heap allocations (or copies) than in a coroutine where most data can just live on the stack.
A coroutine doesn't do anything more than a callback does -- it's just syntactic sugar.
The default behaviour of many asynchronous systems is to extend the lifetime of context data until all the asynchronous handlers have run. You can also just bind them to the resource instead which is arguably more elegant, but which depends on how cancellation is implemented.