Avoid launchIn

Coming from RxJava or other reactive frameworks like LiveData, launchIn(CoroutineScope) is an attractive tool. To see why, take the following example:

    disposables.add(events.filterIsInstance<CloseClicked>()
      .doOnEach { navigator.goTo(Finish()) }
      .subscribe()

In coroutines, launchIn allows me to write the same thing in the same way, but better:

    events.filterIsInstance<CloseClicked>()
      .onEach { navigator.goTo(Finish()) }
      .launchIn(coroutineScope)

This code can be written in all the same places as the Rx version, but it is better:

And yet launchIn is bad and you should avoid it.

launchIn is a conceptual trap

The problem is that coroutines aren't callbacks, like LiveData and RxJava are. They're better than callbacks.

You can also write the above example like this:

    coroutineScope.launch {
      events.first { it is CloseClicked }
      navigator.goTo(Finish())
    }

This is better than either example above.

Write suspend funs, not async callbacks

What about the exact non-launchIn equivalent, though?

    coroutineScope.launch {
      events.filterIsInstance<CloseClicked>()
        .collect { navigator.goTo(Finish()) }
    }

Even here you are better served with this code because the inner block is straightline code written in a suspend context.

Prior to coroutines, straightline code was worse than async callback code. "Don't write blocking code," we learned. "Wrap blocking code in something you can run in the background." And by "run in the background," we meant "wrap it in some kind of callback" — AsyncTask, RxJava, LiveData, Futures, Volley, or even our dear old friend Thread.

Coroutines flip this relation upside down. It is trivial to run a suspend fun call concurrently. You call launch in a CoroutineScope — done. It is more work to turn a callback written with launchIn into a suspend fun.

So don't use launchIn — use collect { ... } within a coroutine instead. You will avoid a local maxima, and grander vistas will stretch out before you.

(Avoid parameterless collect(), too — but I'll leave the reasons why as an exercise for the reader.)

2021/03/27
-Up-^