Using runBlocking in Kotlin Coroutines when targeting JavaScript? - kotlin

Is there a way to write the Kotlin code below so that it compiles and works the same way on the JVM and in JavaScript?
fun <A: Any> request(request: Any): A = runBlocking {
suspendCoroutine<A> { cont ->
val subscriber = { response: A ->
cont.resume(response)
}
sendAsync(request, subscriber)
}
}
fun <Q : Any, A : Any> sendAsync(request: Q, handler: (A) -> Unit) {
// request is sent to a remote service,
// when the result is available it is passed to handler(... /* result */)
}
The code compiles and works fine when compiled to target the JVM.
A compilation error is emitted when targeting JavaScript due to non-existent function runBlocking

Your main problem is that you aren't asking for the thing you actually need. The code you wrote starts a coroutine, suspends it, then blocks until it's done. This is exactly equivalent to having no coroutines at all and just making a blocking network request, which is something you can't possibly expect JavaScript to allow you.
What you actually have to do is step back to the call site of request() and wrap it in a launch:
GlobalScope.launch(Dispatchers.Default) {
val result: A = request(...)
// work with the result
}
With this in place you can rewrite your request function to
suspend fun <A: Any> request(request: Any): A = suspendCancellableCoroutine {
sendAsync(request, it::resume)
}

Related

In Kotlin, is it possible to substitute a suspend fun with a non-suspend version, without breaking the caller?

I'm learning concurrency in Kotlin, coming from C#/JavaScript background, and I can't help comparing some concepts.
In C# and JavaScript, technically we can rewrite an async function as a regular non-async version doing the same thing, using Task.ContinueWith or Promise.then etc.
The caller of the function wouldn't even notice the difference (I ranted about it in a blog post).
Is something like that possible for a suspend function in Kotlin (i.e., without changing the calling code)? I don't think it is, but I thought I'd still ask.
The closest thing I could come up with is below (Kotlin playground link), I still have to call .await():
import kotlinx.coroutines.*
suspend fun suspendableDelay(ms: Long): Long {
delay(ms);
return ms;
}
fun regularDelay(ms: Long): Deferred<Long> {
val d = CompletableDeferred<Long>()
GlobalScope.async { delay(ms); d.complete(ms) }
return d;
}
suspend fun test(ms: Long): Long {
delay(ms);
return ms;
}
fun main() {
val r1 = runBlocking { suspendableDelay(250) }
println("suspendableDelay ended: $r1");
val r2 = runBlocking { regularDelay(500).await() }
println("regularDelay ended: $r2");
}
https://pl.kotl.in/_AmzanwcB
If you're on JVM 8 or higher, you can make a function that calls the suspend function in an async job and returns a CompletableFuture, which can be used to get your result with a callback (thenApplyAsync()) or synchronously (get()).
val scope = CoroutineScope(SupervisorJob())
suspend fun foo(): Int {
delay(500)
return Random.nextInt(10)
}
fun fooAsync(): CompletableFuture<Int> = scope.async { foo() }.asCompletableFuture()
fun main() {
fooAsync()
.thenApplyAsync { println(it) }
Thread.sleep(1000)
}
The above requires the kotlinx-coroutines-jdk8 library.
I don't know of a solution that works across multiple platforms.
This can only work if you change your suspending function to a non-suspending blocking function, for example
private fun method(){
GlobalScope.launch {
val value = getInt()
}
}
// Calling coroutine can be suspended and resumed when result is ready
private suspend fun getInt(): Int{
delay(2000) // or some suspending IO call
return 5;
}
// Calling coroutine can't be suspended, it will have to wait (block)
private fun getInt(): Int{
Thread.sleep(2000) // some blocking IO
return 5;
}
Here you can simply use the non-suspending version, without any change on the caller.
But the issue here is that without suspend modifier the function becomes blocking and as such it can not cause the coroutine to suspend, basically throwing away the advantage of using coroutiens.

Mix and match Coroutines and Rxjava

Coroutines and RxJava3
I have the following method that first makes a call to a suspend method and in the same launch scope I make 2 calls to RxJava.
I am wondering if there is a way to remove the Rxjava code out of the viewModelScope.launch scope and return the result of fetchRecentUseCase.execute().
Basically, is it possible for the viewModelScope.launch to return the listOfProducts rather than doing everything in the launch scope?
fun loadRecentlyViewed() {
viewModelScope.launch {
val listOfProducts = withContext(Dispatchers.IO) {
fetchRecentUseCase.execute()
}
val listOfSkus = listOfProducts.map { it.sku }
if (listOfSkus.isNotEmpty()) {
loadProductUseCase.execute(listOfSkus)
.subscribeOn(schedulersFacade.io)
.flatMap(convertProductDisplayUseCase::execute)
.map { /* work being done */ }
.observeOn(schedulersFacade.ui)
.subscribeBy(
onError = Timber::e,
onSuccess = { }
)
}
}
}
Usecase for the suspend method
class FetchRecentUseCaseImp() {
override suspend fun execute(): List<Products> {
// Call to network
}
}
Many thanks in advance
With coroutines, the way to return a single item that is produced asynchronously is to use a suspend function. So instead of launching a coroutine, you mark the function as suspend and convert blocking or async callback functions into non-blocking code.
The places where coroutines are launched are typically at UI interactions (click listeners), or when classes are first created (on Android, this is places like in a ViewModel constructor or Fragment's onViewCreated()).
As a side note, it is against convention for any suspend function to expect the caller to have to specify a dispatcher. It should internally delegate if it needs to, for example:
class FetchRecentUseCaseImp() {
override suspend fun execute(): List<Products> = withContext(Dispatchers.IO) {
// Synchronous call to network
}
}
But if you were using a library like Retrofit, you'd simply make your Request and await() it without specifying a dispatcher, because await() is a suspend function itself.
So your function should look something like:
suspend fun loadRecentlyViewed(): List<SomeProductType> {
val listOfSkus = fetchRecentUseCase.execute().map(Product::sku)
if (listOfSkus.isEmpty()) {
return emptyList()
}
return runCatching {
loadProductUseCase.execute(listOfSkus) // A Single, I'm assuming
.await() // Only if you're not completely stripping Rx from project
.map { convertProductDisplayUseCase.execute(it).await() } // Ditto for await()
.toList()
.flatten()
}.onFailure(Timber::e)
.getOrDefault(emptyList())
}

Why does a normal function need to be wrapped with viewModelScope.launch?

The following code is from the project.
1: In my mind,a suspend fun should be launched in another suspend fun or viewModelScope.launch{ }, withContext{ } ... , filterItems() is only a normal function, I don't know why filterItems() need to be wrapped with viewModelScope.launch{ } in the function filterTasks(), could you tell me ?
2: In the function filterTasks(), viewModelScope.launch{ } will launch in coroutines, it's asynchronous, I think return result maybe be launched before I get the result from viewModelScope.launch{}, so the result maybe null, is the code correct?
Code
private fun filterTasks(tasksResult: Result<List<Task>>): LiveData<List<Task>> {
val result = MutableLiveData<List<Task>>()
if (tasksResult is Success) {
isDataLoadingError.value = false
viewModelScope.launch {
result.value = filterItems(tasksResult.data, getSavedFilterType())
//return filterItems(tasksResult.data, getSavedFilterType()) //It will cause error.
}
} else {
result.value = emptyList()
showSnackbarMessage(R.string.loading_tasks_error)
isDataLoadingError.value = true
}
return result //I think it maybe be launched before I get the result from viewModelScope.launch{}
}
private fun filterItems(tasks: List<Task>, filteringType: TasksFilterType): List<Task> {
val tasksToShow = ArrayList<Task>()
// We filter the tasks based on the requestType
for (task in tasks) {
when (filteringType) {
ALL_TASKS -> tasksToShow.add(task)
ACTIVE_TASKS -> if (task.isActive) {
tasksToShow.add(task)
}
COMPLETED_TASKS -> if (task.isCompleted) {
tasksToShow.add(task)
}
}
}
return tasksToShow
}
It doesn't, unless it performs some heavy work and you want to move it to a background thread, which is the case here. Here the author just wanted to disjoint the work so the live data can be updated with an empty list first, and the filtered list later(computationally intensive to get), but forgot to do it out of the main thread.
In this particular case the author may have forgotten to add a background dispatcher as a parameter
viewModelScope.launch(Dispatchers.Default)
hence, in this scenario the intended behavior was not achieved, so you see this "nonsensical" coroutine.
I think you can contribute to the project with a fix :)
yes, you are right. but if you looked up the implementation of the launch {} such in lifecycleScope.launch {} or viewModelScope.launch {} you would find out the "block" which is "the coroutine code which will be invoked in the context of the provided scope" is cast to be suspend, so any block of code between launch {} is suspend code block. so in your example filterItems is cast to suspend under the hood and it's wrapped with viewModelScope.launch{ } to do its heavy task not in main thread.
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
// the below line is doing the magic
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
I agree that the code looks suspicious, main reason is that it launches the filterItems coroutine into the Main dispatcher, basically just postponing the moment when filterItems will run on the GUI thread. If filterItems takes long to complete, it will block the GUI; if it doesn't take long, then why would you launch a concurrent coroutine in the first place?
Furthermore, on an architectural level, I don't see a reason why you'd have a function returning LiveData<List<Task>> when you can just have a suspend fun returning List<Task>.

How to Resolve Function Template Generics for Signal/Slot System?

I'm trying to develop a simplistic signals/slots system in Kotlin. Here's what I have so far:
open class Signal<T : Function<Unit>>() {
val callbacks = mutableListOf<T>()
open fun addCallback(slot: T) {
callbacks.add(slot)
}
open fun emit(vararg params: Any) {
for(call in callbacks) {
call(*params)
}
}
}
fun test(myarg: Int) = println(myarg)
fun main(args: Array<String>) {
val myevent = Signal<(Int) -> Unit>()
myevent.addCallback(::test)
myevent.emit(2)
}
The idea is one would create an instance of Signal along with a generic template to dictate which parameters are used for the callbacks. Callbacks can then be added to the Signal. Finally, whenever the Signal needs to be... well... "signaled", the emit method is used. This method passes all the parameters to the corresponding callbacks if necessary.
The issue is this code results in the following error:
kotlin\Signal.kt:30:4: error: expression 'call' of type 'T' cannot be invoked as a function. The function 'invoke()' is not found
The line in question is:
call(*params)
Any recommendations on how to handle things from here?
This is because Function is an empty interface (source).
The various function types that actually have invoke operators are all defined one by one here, as Function0, Function1, etc.
I don't think you'll be able to create a Signal implementation that may have callbacks with any number and any type of parameters. Could you perhaps get by with only having callbacks with a single parameter?
open class Signal<T> {
val callbacks = mutableListOf<(T) -> Unit>()
open fun addCallback(slot: (T) -> Unit) {
callbacks.add(slot)
}
open fun emit(param: T) {
for (call in callbacks) {
call(param)
}
}
}
fun test(myarg: Int) = println(myarg)
fun main(args: Array<String>) {
val myevent = Signal<Int>()
myevent.addCallback(::test)
myevent.emit(2)
}
(Note that you could replace both usages of (T) -> Unit here with Function1<T, Unit>.)

How to unit-test Kotlin-JS code with coroutines?

I've created a multi-platform Kotlin project (JVM & JS), declared an expected class and implemented it:
// Common module:
expect class Request(/* ... */) {
suspend fun loadText(): String
}
// JS implementation:
actual class Request actual constructor(/* ... */) {
actual suspend fun loadText(): String = suspendCoroutine { continuation ->
// ...
}
}
Now I'm trying to make a unit test using kotlin.test, and for the JVM platform I simply use runBlocking like this:
#Test
fun sampleTest() {
val req = Request(/* ... */)
runBlocking { assertEquals( /* ... */ , req.loadText()) }
}
How can I reproduce similar functionality on the JS platform, if there is no runBlocking?
Mb it's late, but there are open issue for adding possibility to use suspend functions in js-tests (there this function will transparent convert to promise)
Workaround:
One can define in common code:
expect fun runTest(block: suspend () -> Unit)
that is implemented in JVM with
actual fun runTest(block: suspend () -> Unit) = runBlocking { block() }
and in JS with
actual fun runTest(block: suspend () -> Unit): dynamic = promise { block() }
TL;DR
On JS one can use GlobalScope.promise { ... }.
But for most use cases the best option is probably to use runTest { ... } (from kotlinx-coroutines-test), which is cross-platform, and has some other benefits over runBlocking { ... } and GlobalScope.promise { ... } as well.
Full answer
I'm not sure what things were like when the question was originally posted, but nowadays the standard, cross-platform way to run tests that use suspend functions is to use runTest { ... } (from kotlinx-coroutines-test).
Note that in addition to running on all platforms, this also includes some other features, such as skipping delays (with the ability to mock the passage of time).
If for any reason (which is not typical, but might sometimes be the case) it is actually desirable to run the code in the test as it runs in production (including actual delays), then runBlocking { ... } can be used on JVM and Native, and GlobalScope.promise { ... } on JS. If going for this option, it might be convenient to define a single function signature which uses runBlocking on JVM and Native, and GlobalScope.promise on JS, e.g.:
// Common:
expect fun runTest(block: suspend CoroutineScope.() -> Unit)
// JS:
#OptIn(DelicateCoroutinesApi::class)
actual fun runTest(block: suspend CoroutineScope.() -> Unit): dynamic = GlobalScope.promise(block=block)
// JVM, Native:
actual fun runTest(block: suspend CoroutineScope.() -> Unit): Unit = runBlocking(block=block)
I was able to make the following work:
expect fun coTest(timeout: Duration = 30.seconds, block: suspend () -> Unit): Unit
// jvm
actual fun coTest(timeout: Duration, block: suspend () -> Unit) {
runBlocking {
withTimeout(timeout) {
block.invoke()
}
}
}
// js
private val testScope = CoroutineScope(CoroutineName("test-scope"))
actual fun coTest(timeout: Duration, block: suspend () -> Unit): dynamic = testScope.async {
withTimeout(timeout) {
block.invoke()
}
}.asPromise()
This launches a co-routine in a scope of your choice using async which you can then return like a promise.
You then write a test like so:
#Test
fun myTest() = coTest {
...
}