-
Notifications
You must be signed in to change notification settings - Fork 4.1k
update: updating coroutines landing page for coroutines restructuring #4802
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love the new intro! nice and clear
docs/topics/coroutines-overview.md
Outdated
desktop, or mobile applications, it's important to provide an experience that is not only fluid from the user's perspective, | ||
but also scalable when needed. | ||
Applications often need to perform multiple tasks at the same time, such as responding to user input, loading data, or updating the screen. | ||
To support this, applications rely on asynchronous programming, which allows operations to run independently without blocking each other. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To support this, applications rely on asynchronous programming, which allows operations to run independently without blocking each other. | |
To support this, they rely on asynchronous programming, which allows operations to run independently without blocking each other. |
docs/topics/coroutines-overview.md
Outdated
- [Coroutine context and dispatchers](coroutine-context-and-dispatchers.md) | ||
- [Shared mutable state and concurrency](shared-mutable-state-and-concurrency.md) | ||
- [Asynchronous flow](flow.md) | ||
## Add the kotlinx.coroutines library to your project |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move to Basics (Create your first coroutine) page
docs/topics/coroutines-overview.md
Outdated
such as concurrency and actors. | ||
In Kotlin, asynchronous programming is built around _coroutines_, which let you write asynchronous code in a natural, sequential style using suspending functions. | ||
Coroutines are lightweight alternatives to threads. | ||
They can suspend without blocking system resources and are cheap to create, making them better suited for fine-grained concurrency. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They can suspend without blocking system resources and are cheap to create, making them better suited for fine-grained concurrency. | |
They can suspend without blocking system resources and are easy to create, making them better suited for fine-grained concurrency. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm 🤔 although they are easy to create I'm using cheap to further emphasize the point about them being lightweight - let's go with resource-friendly for clarity?
docs/topics/coroutines-overview.md
Outdated
|
||
Builder functions like `launch()` and `async()` automatically create a set of elements that define how the coroutine behaves: | ||
|
||
* [`Job`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/) interface: Tracks the coroutine’s lifecycle and enables structured concurrency. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* [`Job`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/) interface: Tracks the coroutine’s lifecycle and enables structured concurrency. | |
* The [`Job`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/) interface tracks the coroutine's lifecycle and enables structured concurrency. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good idea 👍 - I like this version much better
docs/topics/coroutines-overview.md
Outdated
* [`CoroutineDispatcher`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/): Controls where the coroutine runs, such as on a background thread or the main thread in UI applications. | ||
* [`CoroutineExceptionHandler`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/): Handles uncaught exceptions. | ||
|
||
Together, these elements make up the [_coroutine context_](coroutine-context-and-dispatchers.md), which is inherited by default from the coroutine’s parent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Together, these elements make up the [_coroutine context_](coroutine-context-and-dispatchers.md), which is inherited by default from the coroutine’s parent. | |
Together, these elements make up the [_coroutine context_](coroutine-context-and-dispatchers.md), which is inherited by default from the coroutine's parent. |
docs/topics/coroutines-overview.md
Outdated
|
||
* Learn the fundamentals of coroutines, suspending functions, and builders in the [Coroutine basics guide](coroutines-basics.md). | ||
* Explore how to combine suspending functions and build coroutine pipelines in [Composing suspending functions](coroutine-context-and-dispatchers.md). | ||
* Learn how to [debug coroutines](debug-coroutines-with-idea.md) using the built-in tools in IntelliJ IDEA. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Learn how to [debug coroutines](debug-coroutines-with-idea.md) using the built-in tools in IntelliJ IDEA. | |
* Learn how to [debug coroutines](debug-coroutines-with-idea.md) using built-in tools in IntelliJ IDEA. |
docs/topics/coroutines-overview.md
Outdated
* Explore how to combine suspending functions and build coroutine pipelines in [Composing suspending functions](coroutine-context-and-dispatchers.md). | ||
* Learn how to [debug coroutines](debug-coroutines-with-idea.md) using the built-in tools in IntelliJ IDEA. | ||
* For flow-specific debugging, see [Debug Kotlin Flow using IntelliJ IDEA - tutorial](debug-flow-with-idea.md). | ||
* See how to write unit tests for coroutine-based code on Android in [Testing Kotlin coroutines on Android](https://developer.android.com/kotlin/coroutines/test). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplication of "on android", consider rephrasing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, regarding Android materials, do you think we should focus on testing for Android here? the original page had a couple of other related links
docs/topics/coroutines-overview.md
Outdated
### Documentation | ||
> Check out these sample projects to see how coroutines are used in practice: | ||
> | ||
> * [kotlinx.coroutines examples and sources](https://github.com/Kotlin/kotlin-coroutines/tree/master/examples) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This repo is archived. Is there a similar examples collection in https://github.com/Kotlin/kotlinx.coroutines
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After asking the team I removed it and only left in the kotlinconf app
docs/topics/coroutines-overview.md
Outdated
|
||
To launch new coroutines, you can use coroutine builders like [`launch()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html) and [`async()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both launch
and async
have a CoroutineScope
as the receiver, which is very important and has not been introduced by this point. Simply trying to call launch
will not compile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 Great point :
What do you think about slightly restructuring the beginning of this section to something like this, would this be better 🤔 :
Coroutines always run in a CoroutineScope
, which defines their lifecycle and provides the coroutine context.
Builder functions like launch()
and async()
are extension functions on CoroutineScope
. When called, they create a coroutine and automatically add a set of elements that define how it behaves:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
docs/topics/coroutines-overview.md
Outdated
|
||
Kotlin solves this problem in a flexible way by providing [coroutine](https://en.wikipedia.org/wiki/Coroutine) support | ||
at the language level and delegating most of the functionality to libraries. | ||
The most common way to run tasks asynchronously is by using threads, which are independent paths of execution managed by the operating system. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is absolutely correct, but unfortunately, also very misleading, due to how the meaning of the word "asynchronous" shifted over time. Multithreading is an example of asynchronous computations, no doubt, but today, in most cases, "asynchronous programming" means a very specific thing (which kotlinx.coroutines
is an example of): along with the request for some thread to do something, also include code that should run after that operation has completed.
Typically, this looks something like this:
// this function returns immediately, and when `readFile` has finished,
// it also schedules `println` to be called
executeOnIoThread(
whatToExecute = { readFile() },
andThen = { contents -> println(contents) })
Kotlin's suspend
functions allow writing something to the same effect as if it was linear code:
val contents = withContext(Dispatchers.IO) {
readFile() // executes on the IO thread
}
// actually, `withContext` returns immediately ("suspends"),
// but after `readFile()` finishes, it schedules the subsequent code to be called:
println(contents)
So, this is what's called "asynchronous programming".
Instead of "rely on asynchronous programming", I'd say "concurrency", of which threads are indeed an unambiguous example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, that's very helpful 💯
I've restructured the beginning a bit (moving away from asynchronous programming to concurrency/concurrently):
Applications often need to perform multiple tasks at the same time, such as responding to user input, loading data, or updating the screen.
To support this, they rely on concurrency, which allows operations to run independently without blocking each other.The most common way to run tasks concurrently is by using threads, which are independent paths of execution managed by the operating system.
However, threads are relatively heavy, and creating many of them can lead to performance issues.To support efficient concurrency, Kotlin uses asynchronous programming built around coroutines, which let you write asynchronous code in a natural, sequential style using suspending functions.
Coroutines are lightweight alternatives to threads.
docs/topics/coroutines-overview.md
Outdated
While suspending functions return only a single result, flows let you emit and collect multiple values over time without blocking a thread. | ||
For more information, see [Asynchronous flow](flow.md). | ||
|
||
For direct communication between coroutines, use [`Channel`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/), which allows one coroutine to send values to another. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one coroutine ... to another
Channels are more flexible than that: they allow many coroutines to send values, and for many other coroutines to receive them.
Also, for the use case of one coroutine sending values and the other one receiving them, Flow
is often a better fit.
- One coroutine sending values, one coroutine receiving them: a cold flow can do that in the case when the values are only sent when someone actively wants to receive them (which is the majority of all cases).
- Some coroutines sending values, multiple coroutines receiving every emitted value (a fanout of value emissions): this is what
SharedFlow
is for. - Some coroutines sending values, some coroutines receiving values, each value only going to one of the coroutines: such sharing of work calls for a
Channel
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm 🤔
Based on that I'm thinking of something like this - what is your opinion?
Asynchronous flow and shared mutable state
Kotlin Coroutines provides the
Flow
interface for working with asynchronous streams of values that one coroutine produces and another collects.
This lets the collecting coroutine process values without blocking a thread.
For more information, see Asynchronous flow.Use
Channel
when you want several coroutines to send and receive values with each value delivered to only one coroutine.
See Channels and the Coroutines and channels tutorial for more information.When multiple coroutines need to access or update the same data, they share mutable state.
Without coordination, this can lead to race conditions, where operations interfere with each other in unpredictable ways.
To safely manage shared mutable state, useStateFlow
to wrap the shared data.
Then, you can update it from one coroutine and collect its latest value from others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works as a practical approximation, yes. We can keep it like this, and it will already be okay, or we could polish it some more.
This is a bit misleading:
Use Flow when you want a coroutine to consume a stream of values produced by another coroutine.
It is not necessarily another coroutine. It could be the same one. It could be several ones. That said, it's often one coroutine, so if you do want to consume values produced by another coroutine, sure, Flow
could fit your use case well.
Here's another potential improvement I see: I'm wary that SharedFlow
is not mentioned, though: everything else is, so for completeness, adding just one more entity could be beneficial. On the other hand, this would become a bit unwieldy if it were longer.
Here's a crystal clear distinction between the ways of communicating across coroutines:
- A cold
Flow
allows exactly one coroutine to receive all values produced just for its sake (who exactly produces the values depends on the specificFlow
construct you use). Channel
allows several coroutines to send values, and several coroutines to receive values, with each value going to exactly a single coroutine.- A hot
Flow
(SharedFlow
) allows several coroutines to receive values, but as opposed to channels, the same value will be broadcast to (shared between) all the coroutines that subscribed to the updates. - A
StateFlow
is a variation ofSharedFlow
(and so ensures that the upcoming values will be broadcast to everyone!) that is specifically tailored to sharing mutable state and setting and obtaining the latest value. Multiple coroutines can update the value.
docs/topics/coroutines-overview.md
Outdated
|
||
When multiple coroutines need to access or update the same data, they _share mutable state_. | ||
Without coordination, this can lead to race conditions, where operations interfere with each other in unpredictable ways. | ||
To safely manage shared mutable state, the `kotlinx.coroutines` library provides constructs like [`Mutex`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/) and atomic variables. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kotlinx.coroutines
does not provide atomic variables, the standard library does (though only recently; before that, kotlinx.atomicfu
was the library providing them). Also, both mutexes and atomic variables are too low-level and error-prone for most scenarios. StateFlow
is very often the better way of sharing mutable state between coroutines, I think it's better to highlight that instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me - let's drop those from the overview then 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would something like this work? 🤔
(In this case I'll comment out the link to the Shared mutable state doc. I'll put it back after we updated that page as well)
When multiple coroutines need to access or update the same data, they share mutable state.
Without coordination, this can lead to race conditions, where operations interfere with each other in unpredictable ways.
To safely manage shared mutable state, useStateFlow
to wrap the shared data.
Then you can update it from one coroutine and collect its latest value from others.
This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to go as the first approximation. I hope we can revisit this once we work through the rest of the sections. After all, the landing page should reflect them properly.
docs/topics/coroutines-overview.md
Outdated
Builder functions like `.launch()` and `.async()` automatically create a set of elements that define how the coroutine behaves: | ||
|
||
* The [`Job`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/) interface tracks the coroutine's lifecycle and enables structured concurrency. | ||
* [`CoroutineDispatcher`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/) controls where the coroutine runs, such as on a background thread or the main thread in UI applications. | ||
* [`CoroutineExceptionHandler`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/) handles uncaught exceptions. | ||
|
||
Together, these elements make up the [_coroutine context_](coroutine-context-and-dispatchers.md), which is inherited by default from the coroutine's parent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are more potential context elements, though, these are only the main ones. To avoid misleading, some form of "and others" should be introduced.
* [`CoroutineExceptionHandler`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/) handles uncaught exceptions. | ||
|
||
Together, these elements make up the [_coroutine context_](coroutine-context-and-dispatchers.md), which is inherited by default from the coroutine's parent. | ||
This context forms a hierarchy that enables structured concurrency, where related coroutines can be [canceled](cancellation-and-timeouts.md) together or [handle exceptions](exception-handling.md) as a group. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This context forms a hierarchy that enables structured concurrency, where related coroutines can be [canceled](cancellation-and-timeouts.md) together or [handle exceptions](exception-handling.md) as a group. | |
This context forms a hierarchy that enables structured concurrency, where related coroutines can be [cancelled](cancellation-and-timeouts.md) together or [handle exceptions](exception-handling.md) as a group. |
I know it's not the correct sort of English, but we already have two "L"s everywhere, so consistency demands this, I'm afraid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm 🤔 I see what you mean - I see both used in the coroutine guides (although you are 100% right that cancelled is generally more used). That said, since the rest of kotlinlang.org/docs consistently uses American English, I’d suggest we align with that and update the spelling to canceled throughout as part of the rewrite.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is, it's not just about the coroutines guide, it's what function names the library exposes as well:
- https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/is-cancelled.html
- https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.coroutines.cancellation/-cancellation-exception/
- https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellable-continuation/
- https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/suspend-cancellable-coroutine.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a conundrum — I'm not sure.. I'll double check with the team! 👍
This update involves restructuring the Coroutines overview into a landing page for KT-76566