Skip to content

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

Merged
merged 7 commits into from
Jun 20, 2025

Conversation

daniCsorbaJB
Copy link
Contributor

This update involves restructuring the Coroutines overview into a landing page for KT-76566

@daniCsorbaJB daniCsorbaJB requested a review from a team as a code owner April 11, 2025 09:41
Copy link
Contributor

@danil-pavlov danil-pavlov left a 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

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

- [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
Copy link
Contributor

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

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Copy link
Contributor Author

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?


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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* [`Job`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/) interface: Tracks the coroutines 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.

Copy link
Contributor Author

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

* [`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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Together, these elements make up the [_coroutine context_](coroutine-context-and-dispatchers.md), which is inherited by default from the coroutines parent.
Together, these elements make up the [_coroutine context_](coroutine-context-and-dispatchers.md), which is inherited by default from the coroutine's parent.


* 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* 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.

* 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).
Copy link
Contributor

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

Copy link
Contributor

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

### 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)
Copy link
Contributor

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?

Copy link
Contributor Author

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

@danil-pavlov danil-pavlov self-requested a review April 16, 2025 11:58

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).
Copy link
Contributor

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.

Copy link
Contributor Author

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:

Copy link
Contributor

@dkhalanskyjb dkhalanskyjb May 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!


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.
Copy link
Contributor

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.

Copy link
Contributor Author

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.

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.
Copy link
Contributor

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.

Copy link
Contributor Author

@daniCsorbaJB daniCsorbaJB Jun 17, 2025

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, use StateFlow to wrap the shared data.
Then, you can update it from one coroutine and collect its latest value from others.

Copy link
Contributor

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 specific Flow 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 of SharedFlow (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.


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.
Copy link
Contributor

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.

Copy link
Contributor Author

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 👍

Copy link
Contributor Author

@daniCsorbaJB daniCsorbaJB Jun 16, 2025

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, use StateFlow to wrap the shared data.
Then you can update it from one coroutine and collect its latest value from others.

Copy link
Contributor

github-actions bot commented Jun 2, 2025

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.

@github-actions github-actions bot added the Stale label Jun 2, 2025
@koshachy koshachy removed the Stale label Jun 4, 2025
Copy link
Contributor

@dkhalanskyjb dkhalanskyjb left a 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.

Comment on lines 43 to 49
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.
Copy link
Contributor

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Copy link
Contributor Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

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! 👍

@daniCsorbaJB daniCsorbaJB merged commit 8877cc4 into master Jun 20, 2025
4 checks passed
@daniCsorbaJB daniCsorbaJB deleted the coroutines-overview-rewrite branch June 20, 2025 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants