Let me first state that I understand the use-cases for Coroutines in Android Development and the JVM. iOS I have no experience at all, so can’t say anything about it.
Another disclaimer: I’ve read quite a lot in the past months on Coroutines, but that doesn’t mean I fully understand it. That’s why I place it here for a discussion.
Some background:
I am working on a Kotlin multiplatform project with a JVM and a JS target. The common domain logic needs to be shared in the common module, just like some infrastructure. After going back and forth a bit with React wrappers and whatnot, I’ve settled with just ‘vanilla’ kotlinx.html which works pretty good!
I’ve started reading into Coroutines in the past months and visited a lot of talks at KotlinConf 2019. Nevertheless, I found the concept quite confusing at times, especially when looking at my usecase, where I want to target Kotlin/JS. However, I’ve started getting the feeling that coroutines are solving something that isn’t really an issue in JS anymore and make simple tasks pretty complicated…
1. The main usecase for Coroutines is asynchronous programming
This is also clearly stated in the Coroutines Overview:
Asynchronous or non-blocking programming is the new reality. Whether we’re creating server-side, desktop or mobile applications, it’s important that we provide an experience that is not only fluid from the user’s perspective, but scalable when needed.
…
As a bonus, coroutines not only open the doors to asynchronous programming, but also provide a wealth of other possibilities such as concurrency, actors, etc.
This focus on asynchronicity is also reflected in the documentation where every long running task is modelled with a delay
. These are simulating IO-bound tasks.
However, there are also CPU-bound tasks, which hog a certain thread. These can’t be suspended at all, due to logical reasons, and the only way to solve it is to move them on a different thread.
2. JS doesn’t have multiple threads
JS only has 1 thread. Web workers can be used to create actual multithreaded pages, but they require launching a separate script. I don’t see how Coroutines could help here.
So, this means that coroutines in the JS world are useless if you want to use them for concurrency. Please note, that this isn’t Coroutines fault, but more the architecture of the web.
3. Callback hell is touted as problem to which coroutines are the answer…but really?
So, with threading not really possible, asynchronicity remains. In a lot of documents callback hell is mentioned as something that coroutines solve. Fair enough. But there were already a lot of solutions solving this, including Promises which allow chaining and things like Promise.all
. It has been a long time since I’ve seen a lot of nested callbacks and this issue has been solved already. Sure, you need to write chains of then
.
However, coroutines introduce a whole lot of constructs (scopes, contexts, launch, withContext, async, jobs, couroutineScope, classes implementing CouroutineScope
, suspendCoroutine, callbackCoroutine ec etc) to solve an issue that, in JS IMHO, isn’t really an issue anymore for already a few years. And, when wrongfully using scopes and context and one of the various builders you can have performance that is worse off than just VanillaJS. Promises are almost impossible to do wrong.
Besides that you get a dichotomy between suspend
functions and regular functions where the first one can only be called in a coroutine, whereas a function returning a Promise can be used everywhere and one can decide what to do with the result.
Conclusion
So, unless I am completely overlooking something, which is entirely possible… I have the feeling the coroutines are overly complicated and introduce a whole new range of possible misconfigurations for the JS world.
Ofcourse, you have channels and flows and what not, but these solve issues that are either not present in JS (multithreading) or already solved elsewhere (RxJS) and I don’t see any benefits of introducing a completely different library.