-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Deferred has no map function #342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The coroutines philosophy is to design the software architecture around If you follow this philosophy, you'll never need to Example: // don't do:
fun foo(): Deferred<Int> = TODO()
fun bar(arg: Int): Deferred<Int> = TODO()
val deferred = foo().flatMap { bar(it) }.map { it + 1 }
// do:
suspend fun foo(): Int = TODO()
suspend fun bar(arg: Int): Int = TODO()
val deferred = async { bar(foo()) + 1 } I believe this is the reason why there is no promise-like operators in kotlinx.coroutines. Note that one may not follow this philosophy and create |
Le me add a note explaining why suspending function are preferable to functions returning Futures (incl. Deferred, Job, and others): Marking a function So, the whole point of Kotlin coroutines is to not use callback and future anymore. Instead we use suspending functions which lets us write code like sequential code while keeping the benefits of future and callbacks. Therefore declaring a function Consider this Java code: public CompletableFuture<Integer> foo() { ... }
public CompletableFuture<Integer> bar(int arg) { ... }
public CompletableFuture<Object> usage() {
return foo().thenCompose(x -> bar(x)).thenApply(it -> it + 1);
} In this code we have to use Future, combine, map etc. or, because there is no better choice. But the point of kotlin coroutines is to provide the So here is the Kotlin idiomatic equivalent of the Java code above: suspend fun foo(): Int { ... }
suspend fun bar(arg: Int) { ... }
suspend fun usage() = bar(foo()) + 1 |
Closing this issue. Thanks to @jcornaz for quite a thorough explanation. |
Dart has a advanced Asynchrony support: Dart suggest Handling Futures,Handling Streams. foo() async { ... }
bar(arg: Int) async { ... }
usage() async => (await bar((await foo()))) + 1 |
@SolomonSun2010 Thank. We've studied Dart design while working on Kotlin coroutines and we had put quite a lot of thought into making sure we don't fall into the same futures trap as Dart did. As a result, the Dart example you've given looks much simpler (and nicer) in Kotlin:
|
I completely disagree with the notion that one should favor suspend functions and avoid async and Deferred. There is a huge disadvantage of a suspend function is that it is blocking while an async function is not. Async does not block until you call await. So let's say you have two functions for a call to REST server and we need to call both of them and then combine them. So imagine that we do it with suspend:
or we can define them as async functions that return Deferred:
If the 2 API calls take about the same amount of time, the suspend version will take twice as long to execute. In the async case the 2 calls can happen simultaneously instead of sequentially. So there are very valid reasons to favor async over suspend. So Deferred SHOULD have map |
@dalewking We said there is no need for Running stuff in parallel is hard to get right. That's why kotlin coroutines wants us to be explicit about it and use suspend fun restCall1(): String { delay(1000) ; return "Hello " }
suspend fun restCall2(): String { delay(1000) ; return "world!" }
suspend fun callBoth(): List<String> = coroutineScope {
val result1 = async { restCall1() }
val result2 = async { restCall2() }
listOf(result1.await(), result2.await())
} |
Also, just to be clear the point of The following code: suspend fun restCall1(): String { delay(1000) ; return "Hello " } Is compiled to something that is roughy equivalent to (but simplified): fun restCall1(callback: (String) -> Unit) {
delay(1000) { callback("Hello ") }
} If we were to use fun restCall1(): Deferred<String> {
return delay(1000) // <-- Assuming delay would return a deferred
.map { "Hello " }
} But, thanks to coroutines, we don't need suspend fun restCall1(): String {
delay(1000) // <-- delay is a `suspend` function, which does not block
return "Hello " // <-- No special operator! If something must be done after, then simply do put it on the next line.
} That's the core idea behind kotlin coroutines, and the reason why |
When I said blocking, I did not mean blocking the thread. I simply meant blocking in terms of that the second call is "blocked" from executing until the first call finishes. |
In this example, how should I structure it if I have another function, baz, that also needs to receive the value computed by foo()? suspend fun baz(arg: Int): Int = TODO() Let's say that foo() is very expensive and I only want to compute it once. And suppose also that the decision to invoke bar() and baz() are made separately at different times, so when bar() is invoked, it is not yet known whether baz() needs to be invoked. That is to say, it is not possible to do: val deferred = async {
val fooResult = foo()
bar(fooResult)
baz(fooResult)
} Because at the point in time when we invoke bar() we don't yet know if baz() should also be called. How do I structure this situation with suspend functions? This is of course a very common scenario with deferred values, where a single value computed at one point in time may be needed by multiple other deferred computations each of which may spring into existence at separate points of time in the future. |
@existentialtype, from later in the thread:
With that in mind, here's how your use case could be covered: val fooResult = async { foo() }
val barResult = async { bar(fooResult.await()) }
val bazResult = async { baz(fooResult.await()) } If we provided val fooResult = async { foo() }
val barResult = fooResult.map { bar(it) }
val bazResult = fooResult.map { baz(it) } But what does this code really do? You can't tell just by reading it without knowing how
If we take the signature proposed in the original issue request literally, then So, better not to hide the asynchronous control flow behind higher-order functions that have unclear semantics and save a few characters at most. |
there should be a function
it is easy to implement yourself, but it is a bit confusing that it is missing.
there might be other functions to consider, like
or
The text was updated successfully, but these errors were encountered: