I think the main practical problem with what's been proposed for Fibers is that there is a fundamental limit on how small overhead suspension/resumption can be and how often it happens. For example, given a bounded "blocking" queue, either 1) the producer is faster and gets always suspended on the queue becoming full -> then resumed when at least one slot opens up; or 2) the consumer is faster and gets suspended when the queue is empty -> then resumed when an item becomes available. Both cases will effectively incur the extra overhead per item. I think, for coroutines, suspension involves allocating a holder object for the remainder of the coroutine's code and then performing 1 or 2 atomic operations so the (potentially asynchronous) resume() call can find it. For Fibers, this can be more costly because there will be likely a mandatory task scheduled on the ForkJoinPool all the time which by itself adds wrappers and atomics due the internal workstealing-enabled queue. In contrast, one can think about the async boundary in a Reactive-Streams flow as a batch-suspend and batch-resume, thus the unavoidable task scheduling overhead may be distributed along multiple items produced and consumed. In addition, its cooperative nature means the producers only produce as much as the consumer requested and stop themselves, returning from the depths up to the current thread pool's worker thread which continues with the next task. No stack state has to be preserved but only a handful of operator-specific state information has to be saved to an existing instance most of the time.