Edoardo Luppi
06/28/2023, 1:59 PMfun doSomething(): CompletableFuture<T>
Internally I need to interface with suspending functions, is it considered correct to simply wrap the method content in GlobalScope.future
?
fun doSomething(): CompletableFuture<T> = GlobalScope.future {
mySuspendingFunction()
return ...
}
Should I also wrap it with a withContext
?Sam
06/28/2023, 2:02 PMJoffrey
06/28/2023, 2:03 PMdoSomething()
repeatedly, is it ok to have many coroutines fired?
Basically, using GlobalScope
means answering these questions by saying "I never want to cancel anything, let the coroutines leak forever if they hang". Using the CoroutineScope.future
extension may be a good idea, but the choice of scope is the tricky part.Edoardo Luppi
06/28/2023, 2:03 PMkevin.cianfarini
06/28/2023, 2:04 PMGlobalScope
and instead tie a CoroutineScope
to your class instance which holds this. Then expose a close
or cancel
function on this which cancels running tasks. Eg.
class MyClass {
private val scope = CoroutineScope(Job())
public fun theFuture(): CompletableFuture<T> = scope.future {
mySuspendingFunction()
}
private suspend fun mySuspendingFunction() { ... }
public fun close() = scope.cancel()
}
kevin.cianfarini
06/28/2023, 2:04 PMEdoardo Luppi
06/28/2023, 2:05 PMBasically, usingWouldn't any error be propagated and handled by themeans answering these questions by saying "I never want to cancel anything, let the coroutines leak forever if they hang"GlobalScope
Future
itself?
If something blocks processing inside of the coroutine, wouldn't it be exactly the same as if something blocks inside of a Future?Sam
06/28/2023, 2:05 PMCompletableFuture
? What prevents you from exposing a suspend
function instead?Edoardo Luppi
06/28/2023, 2:06 PMJoffrey
06/28/2023, 2:07 PMWouldn't any error be propagated and handled by the Future itself?Errors, maybe. But I'm talking about cancellation here, and hanging work.
Joffrey
06/28/2023, 2:08 PMDeferred
. It doesn't give the consumer any control over where the work is run. That's why future-based java APIs often come in conjunction with some way to configure an ExecutorService
somewhere, to allow to control that work - cancellation (close()
), threading/pool size, etc.
On the other hand, suspend
functions require consumers to define where to run them. That's why when you bridge futures to suspend functions, you have to define this (a scope for the lifetime, a dispatcher for the threading, etc.).Edoardo Luppi
06/28/2023, 2:11 PMJoffrey
06/28/2023, 2:11 PMDispatchers.Default
or <http://Dispatchers.IO|Dispatchers.IO>
), but when to cancel your work is something you have to decide yourself. The framework just provides you with the tool (CoroutineScope
) to express it with little code. But where you create the scope and where you cancel it is up to you.Edoardo Luppi
06/28/2023, 6:03 PMCoroutineScope
per each platform I support, and then use that expect declaration on my platform-agnostic service where I need to expose the results as Future
and Promise