launch() will schedule the coroutine to run on the current thread (the thread that is performing an infinite loop) if you don’t specify a different dispatcher. But in order for the scheduled coroutine to run, the current coroutine (the one created by runBlocking()) has to suspend. That allows the current thread to run other coroutines scheduled on it. A coroutine created with runBlocking() will suspend when it gets to the end, which is why your second example works. But in your first example, the outer coroutine created by runBlocking() never suspends – it just creates coroutine after coroutine, scheduling them on a thread which will never be allowed to run them.