I'm not being unfair when I enumerate the most popular languages for comparison. When people crap on Javascript, they presumably prefer another language. And C#/Kotlin aren't exactly the top picks.
Kotlin has BYOB coroutines which are hard to work with. People don't use them. Going with the C# approach where async behavior looks sync was a bad move. I predict Kotlin's coroutines will never be a centerpiece abstraction just like how people don't really use Go's channels (people in practice just go back to Mutexes).
I mean, try it. Write the equivalent to this in Kotlin:
// get background work started now
const background = promise()
// crawl some urls concurrently as well, just 4 at a time
const crawl = Promise.map(urls, crawl, { concurrency: 4 })
// while that's going on, we have some work that
// we must get done.
for (const task of tasks) {
await worker(task)
}
// worker's done, now we can wait on
// the crawler and background work.
const [a, b] = await Promise.all([
crawl.then(processResults),
background
])
CSP never caught on because after you have more than one channel as a central bus (the toy architecture), you immediately descend into channel hell. In-channels, out-channels, channels over channels. Back to using pencil and paper and scouring your code to decode the classic buffer bloat problem.
A single-threaded event loop with a central promise abstraction is a great way to write networked code.
Re:Kotlin, I'm not a JVM expert/fan, so I'll take your word for that. As for the lower-level coroutining, isn't the point that this will allow you/library-writers to abstract over these and provide the higher-level abstractions that you want to use?
Regarding "C# approach where async behavior looks sync", I don't follow you here. C# is very explict with async's returning Task<> where with Go's lack of "colored" fns, for example, you don't really know when you code is async or not.
Kotlin has BYOB coroutines which are hard to work with. People don't use them. Going with the C# approach where async behavior looks sync was a bad move. I predict Kotlin's coroutines will never be a centerpiece abstraction just like how people don't really use Go's channels (people in practice just go back to Mutexes).
I mean, try it. Write the equivalent to this in Kotlin:
CSP never caught on because after you have more than one channel as a central bus (the toy architecture), you immediately descend into channel hell. In-channels, out-channels, channels over channels. Back to using pencil and paper and scouring your code to decode the classic buffer bloat problem.A single-threaded event loop with a central promise abstraction is a great way to write networked code.
Btw, I use Kotlin in a large JVM project and I'm stuck with the horror of https://docs.oracle.com/javase/8/docs/api/java/util/concurre.... That's more likely what you'll be doing day to day with Kotlin, not playing with its toy coroutines.