`DefaultDispatcher` - why is this the CommonPool?
See original GitHub issueRxJava is synchronous by default. This behavior is awesome.
observable.subscribe { }
By default, coroutine builders like launch, async, produce, etc use the CommonPool
. In RxJava, that’s like automatically doing:
observable.subscribeOn(Schedulers.computation()).subscribe { }
Because it’s a good pattern for testing and maintainability, I always inject my dispatchers.
launch(dispatchers.background) { }
Unfortunately, the DefaultDispatcher
lets unfamiliar devs easily make the mistake of referring to the common pool. Even worse, they may not even understand what the CommonPool
is and just throw coroutines around and not understand in which dispatcher they run in.
launch { } // surprise! runs in the common pool
Why not make the default dispatcher Unconfined
? That way devs will know that if they want to confine the coroutine to a particular context, they can pass the appropriate dispatcher.
I think it’s a bit confusing to have different coroutine builders refer to different dispatchers.
Note: I understand it’s probably too late to change this behavior 😦
Issue Analytics
- State:
- Created 5 years ago
- Comments:14 (9 by maintainers)
Top GitHub Comments
This is quite opinionated I think. In my opinion,
Unconfined
would have been indeed a good default, for the reasons you mentioned. However, if it is better thanCommonPool
I am less sure.if
launch
andasync
would be unconfined by default, then someone would have writen a similar post to yours with the following snippet:Usually if you use
async
orlaunch
, this is because you want to launch a concurrent coroutine (that’s why it is named like this). So it does make sense to useCommonPool
by default IMO.And I’d like to precise that
subscribe
of RxJava should not be compared tolaunch
orasync
, it has to be compared to its equivalent for coroutines:consumeEach
:We’ve lived with “you must be explicit” for a while, but we had to abandon this policy when we started to work on common code. You cannot require to be explicit in your common code, since JVM has threads and pool, but JS does no, so you cannot just go a write code that runs both on JS and JVM. We needed some default.
What that default should be? It cannot be
Unconfined
for the reasons outlined above (asynchronous code ceases to be asynchronous). Moreover, in JS world it is natural to always dispatch your tasks on your event thread (unconfined is not idiomatic in JS). The best analogue of “the event thread” that we have for JVM is the pool of threads sized to the number of CPU cores, hence it is our default.Now, with structured concurrency (#410) this should not be a big issue anymore. A default is used only when you do
GlobalScope.launch
, which should not be done often (and should never be done in larger projects), so we are kind of back to requiring explicit dispatcher. Now in you apps you need to define your ownCoroutineScope
where you have to define which dispatcher works best for you.