You can also write async code manually in any language, including Java. You can split your code into multiple stages and then use pipe-lines, worker pools, callbacks, or futures. But this is tenuous. The code becomes hard to read, your logic becomes dispersed upon multiple stages and callbacks. Coroutines let you do the same, while still keeping your logic intact.