SlideShare a Scribd company logo
elizarov at JetBrains
Roman Elizarov
Introduction to Coroutines
Asynchronous
programming
How do we write code that waits
for something most of the time?
A toy problem
Kotlin fun requestToken(): Token {
// makes request for a token & waits
return token // returns result when received
}
1
fun requestToken(): Token { … }
fun createPost(token: Token, item: Item): Post {
// sends item to the server & waits
return post // returns resulting post
}
A toy problem
2
Kotlin
fun requestToken(): Token { … }
fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) {
// does some local processing of result
}
A toy problem
3
Kotlin
fun requestToken(): Token { … }
fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
A toy problem
fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
1
2
3
Can be done with
threads!
Kotlin
fun requestToken(): Token {
// makes request for a token
// blocks the thread waiting for result
return token // returns result when received
}
fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
Threads
fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Is anything wrong with it?
How many threads we can have?
100 🙂
How many threads we can have?
1000 😅
How many threads we can have?
10 000 😩
How many threads we can have?
100 000 😵
Callbacks to the rescue
Sort of …
Callbacks: before
fun requestToken(): Token {
// makes request for a token & waits
return token // returns result when received
}
1
Callbacks: after
fun requestTokenAsync(cb: (Token) -> Unit) {
// makes request for a token, invokes callback when done
// returns immediately
}
1
callback
Callbacks: before
fun requestTokenAsync(cb: (Token) -> Unit) { … }
fun createPost(token: Token, item: Item): Post {
// sends item to the server & waits
return post // returns resulting post
}
2
Callbacks: after
fun requestTokenAsync(cb: (Token) -> Unit) { … }
fun createPostAsync(token: Token, item: Item,
cb: (Post) -> Unit) {
// sends item to the server, invokes callback when done
// returns immediately
}
2
callback
Callbacks: before
fun requestTokenAsync(cb: (Token) -> Unit) { … }
fun createPostAsync(token: Token, item: Item,
cb: (Post) -> Unit) { … }
fun processPost(post: Post) { … }
fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Callbacks: after
fun requestTokenAsync(cb: (Token) -> Unit) { … }
fun createPostAsync(token: Token, item: Item,
cb: (Post) -> Unit) { … }
fun processPost(post: Post) { … }
fun postItem(item: Item) {
requestTokenAsync { token ->
createPostAsync(token, item) { post ->
processPost(post)
}
}
}
aka “callback hell”
This is simplified. Handling
exceptions makes it a real mess
Futures/Promises/Rx
to the rescue
Sort of …
Futures: before
fun requestTokenAsync(cb: (Token) -> Unit) {
// makes request for a token, invokes callback when done
// returns immediately
}
1
Futures: after
fun requestTokenAsync(): Promise<Token> {
// makes request for a token
// returns promise for a future result immediately
}
1
future
Futures: before
fun requestTokenAsync(): Promise<Token> { … }
fun createPostAsync(token: Token, item: Item,
cb: (Post) -> Unit) {
// sends item to the server, invokes callback when done
// returns immediately
}
2
Futures: after
fun requestTokenAsync(): Promise<Token> { … }
fun createPostAsync(token: Token, item: Item): Promise<Post> {
// sends item to the server
// returns promise for a future result immediately
}
future
2
Futures: before
fun requestTokenAsync(): Promise<Token> { … }
fun createPostAsync(token: Token, item: Item): Promise<Post> …
fun processPost(post: Post) { … }
fun postItem(item: Item) {
requestTokenAsync { token ->
createPostAsync(token, item) { post ->
processPost(post)
}
}
}
Futures: after
fun requestTokenAsync(): Promise<Token> { … }
fun createPostAsync(token: Token, item: Item): Promise<Post> …
fun processPost(post: Post) { … }
fun postItem(item: Item) {
requestTokenAsync()
.thenCompose { token -> createPostAsync(token, item) }
.thenAccept { post -> processPost(post) }
}
Composable &
propagates exceptions
No nesting indentation
Futures: after
fun requestTokenAsync(): Promise<Token> { … }
fun createPostAsync(token: Token, item: Item): Promise<Post> …
fun processPost(post: Post) { … }
fun postItem(item: Item) {
requestTokenAsync()
.thenCompose { token -> createPostAsync(token, item) }
.thenAccept { post -> processPost(post) }
}
But all those combinators…
Kotlin coroutines to the rescue
Let’s get real
Coroutines: before
fun requestTokenAsync(): Promise<Token> {
// makes request for a token
// returns promise for a future result immediately
}
1
Coroutines: after
suspend fun requestToken(): Token {
// makes request for a token & suspends
return token // returns result when received
}
1
Coroutines: after
suspend fun requestToken(): Token {
// makes request for a token & suspends
return token // returns result when received
}
1
natural signature
Coroutines: before
suspend fun requestToken(): Token { … }
fun createPostAsync(token: Token, item: Item): Promise<Post> {
// sends item to the server
// returns promise for a future result immediately
}
2
Coroutines: after
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post {
// sends item to the server & suspends
return post // returns result when received
}
2
Coroutines: before
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
fun postItem(item: Item) {
requestTokenAsync()
.thenCompose { token -> createPostAsync(token, item) }
.thenAccept { post -> processPost(post) }
}
Coroutines: after
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Coroutines: after
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Like regular code
Coroutines: after
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
suspension
points
• Regular loops
Bonus features
for ((token, item) in list) {
createPost(token, item)
}
• Regular exception handing
Bonus features
try {
createPost(token, item)
} catch (e: BadTokenException) {
…
}
• Regular higher-order functions
• forEach, let, apply, repeat, filter, map, use, etc
Bonus features
file.readLines().forEach { line ->
createPost(token, line.toItem())
}
Everything like in blocking code
Suspending functions
Retrofit async
interface Service {
fun createPost(token: Token, item: Item): Call<Post>
}
Retrofit async
interface Service {
fun createPost(token: Token, item: Item): Call<Post>
}
future
Retrofit async
interface Service {
fun createPost(token: Token, item: Item): Call<Post>
}
suspend fun createPost(token: Token, item: Item): Post =
serviceInstance.createPost(token, item).await()
Retrofit async
interface Service {
fun createPost(token: Token, item: Item): Call<Post>
}
suspend fun createPost(token: Token, item: Item): Post =
serviceInstance.createPost(token, item).await()
natural signature
Retrofit async
interface Service {
fun createPost(token: Token, item: Item): Call<Post>
}
suspend fun createPost(token: Token, item: Item): Post =
serviceInstance.createPost(token, item).await()
Suspending extension function
from integration library
Composition
Beyond sequential
val post = createPost(token, item)
Higher-order functions
val post = retryIO {
createPost(token, item)
}
Higher-order functions
val post = retryIO { createPost(token, item) }
suspend fun <T> retryIO(block: suspend () -> T): T {
var curDelay = 1000L // start with 1 sec
while (true) {
try {
return block()
} catch (e: IOException) {
e.printStackTrace() // log the error
}
delay(curDelay)
curDelay = (curDelay * 2).coerceAtMost(60000L)
}
}
Higher-order functions
val post = retryIO { createPost(token, item) }
suspend fun <T> retryIO(block: suspend () -> T): T {
var curDelay = 1000L // start with 1 sec
while (true) {
try {
return block()
} catch (e: IOException) {
e.printStackTrace() // log the error
}
delay(curDelay)
curDelay = (curDelay * 2).coerceAtMost(60000L)
}
}
Higher-order functions
val post = retryIO { createPost(token, item) }
suspend fun <T> retryIO(block: suspend () -> T): T {
var curDelay = 1000L // start with 1 sec
while (true) {
try {
return block()
} catch (e: IOException) {
e.printStackTrace() // log the error
}
delay(curDelay)
curDelay = (curDelay * 2).coerceAtMost(60000L)
}
}
suspending lambda
Higher-order functions
val post = retryIO { createPost(token, item) }
suspend fun <T> retryIO(block: suspend () -> T): T {
var curDelay = 1000L // start with 1 sec
while (true) {
try {
return block()
} catch (e: IOException) {
e.printStackTrace() // log the error
}
delay(curDelay)
curDelay = (curDelay * 2).coerceAtMost(60000L)
}
}
Everything like in blocking code
Higher-order functions
val post = retryIO { createPost(token, item) }
suspend fun <T> retryIO(block: suspend () -> T): T {
var curDelay = 1000L // start with 1 sec
while (true) {
try {
return block()
} catch (e: IOException) {
e.printStackTrace() // log the error
}
delay(curDelay)
curDelay = (curDelay * 2).coerceAtMost(60000L)
}
}
Coroutine builders
Coroutines revisited
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Coroutines revisited
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Coroutines revisited
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Error: Suspend function 'requestToken' should be called only from
a coroutine or another suspend function
Coroutines revisited
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Can suspend execution
Coroutines revisited
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Can suspend executionA regular function cannot
Coroutines revisited
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Can suspend executionA regular function cannot
One cannot simply invoke
a suspending function
Launch
fun postItem(item: Item) {
launch {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
}
coroutine builder
fun postItem(item: Item) {
launch {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
}
Fire and forget!
Returns immediately, coroutine
works in background thread pool
fun postItem(item: Item) {
launch {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
}
fun postItem(item: Item) {
launch(UI) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
}
UI Context
Just specify the context
fun postItem(item: Item) {
launch(UI) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
}
UI Context
And it gets executed on UI thread
Where’s the magic of launch?
fun launch(
context: CoroutineContext = DefaultDispatcher,
block: suspend () -> Unit
): Job { … }
A regular function
fun launch(
context: CoroutineContext = DefaultDispatcher,
block: suspend () -> Unit
): Job { … } suspending lambda
fun launch(
context: CoroutineContext = DefaultDispatcher,
block: suspend () -> Unit
): Job { … }
async / await
Kotlin-way
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Kotlin
suspend fun requestToken(): Token { … }
suspend fun createPost(token: Token, item: Item): Post { … }
fun processPost(post: Post) { … }
async Task postItem(Item item) {
var token = await requestToken();
var post = await createPost(token, item);
processPost(post);
}
Classic-way
C# approach to the same problem
(also Python, TS, Dart, coming to JS)
C#
async Task<Token> requestToken() { … }
async Task<Post> createPost(Token token, Item item) { … }
void processPost(Post post) { … }
async Task postItem(Item item) {
var token = await requestToken();
var post = await createPost(token, item);
processPost(post);
}
Classic-way
mark with async
C#
async Task<Token> requestToken() { … }
async Task<Post> createPost(Token token, Item item) { … }
void processPost(Post post) { … }
async Task postItem(Item item) {
var token = await requestToken();
var post = await createPost(token, item);
processPost(post);
}
Classic-way
use await to suspend
C#
async Task<Token> requestToken() { … }
async Task<Post> createPost(Token token, Item item) { … }
void processPost(Post post) { … }
async Task postItem(Item item) {
var token = await requestToken();
var post = await createPost(token, item);
processPost(post);
}
Classic-way
C#
returns a future
async Task<Token> requestToken() { … }
async Task<Post> createPost(Token token, Item item) { … }
void processPost(Post post) { … }
Why no await keyword in Kotlin?
The problem with async
requestToken() VALID –> produces Task<Token>
await requestToken() VALID –> produces Token
concurrent behavior
sequential behavior
C#
C#
default
Kotlin suspending functions
are designed to imitate
sequential behavior
by default
Concurrency is hard
Concurrency has to be explicit
Kotlin approach to async
Concurrency where you need it
Use-case for async
async Task<Image> loadImageAsync(String name) { … }C#
Use-case for async
var promise1 = loadImageAsync(name1);
var promise2 = loadImageAsync(name2);
async Task<Image> loadImageAsync(String name) { … }
Start multiple operations
concurrently
C#
Use-case for async
var promise1 = loadImageAsync(name1);
var promise2 = loadImageAsync(name2);
var image1 = await promise1;
var image2 = await promise2;
async Task<Image> loadImageAsync(String name) { … }
and then wait for them
C#
Use-case for async
var result = combineImages(image1, image2);
C#
var promise1 = loadImageAsync(name1);
var promise2 = loadImageAsync(name2);
var image1 = await promise1;
var image2 = await promise2;
async Task<Image> loadImageAsync(String name) { … }
Kotlin async function
fun loadImageAsync(name: String): Deferred<Image> =
async { … }
Kotlin
Kotlin async function
fun loadImageAsync(name: String): Deferred<Image> =
async { … }
Kotlin
A regular function
Kotlin async function
fun loadImageAsync(name: String): Deferred<Image> =
async { … }
Kotlin’s future type
Kotlin
Kotlin async function
fun loadImageAsync(name: String): Deferred<Image> =
async { … }
async coroutine builder
Kotlin
Kotlin async function
fun loadImageAsync(name: String): Deferred<Image> =
async { … }
val deferred1 = loadImageAsync(name1)
val deferred2 = loadImageAsync(name2)
Start multiple operations
concurrently
Kotlin
Kotlin async function
fun loadImageAsync(name: String): Deferred<Image> =
async { … }
val deferred1 = loadImageAsync(name1)
val deferred2 = loadImageAsync(name2)
val image1 = deferred1.await()
val image2 = deferred2.await()
and then wait for them
await function
Suspends until deferred is complete
Kotlin
Kotlin async function
fun loadImageAsync(name: String): Deferred<Image> =
async { … }
val deferred1 = loadImageAsync(name1)
val deferred2 = loadImageAsync(name2)
val image1 = deferred1.await()
val image2 = deferred2.await()
val result = combineImages(image1, image2)
Kotlin
Using async function when needed
suspend fun loadImage(name: String): Image { … }
Is defined as suspending function, not async
Using async function when needed
suspend fun loadImage(name: String): Image { … }
suspend fun loadAndCombine(name1: String, name2: String): Image {
val deferred1 = async { loadImage(name1) }
val deferred2 = async { loadImage(name2) }
return combineImages(deferred1.await(), deferred2.await())
}
Using async function when needed
suspend fun loadImage(name: String): Image { … }
suspend fun loadAndCombine(name1: String, name2: String): Image {
val deferred1 = async { loadImage(name1) }
val deferred2 = async { loadImage(name2) }
return combineImages(deferred1.await(), deferred2.await())
}
Using async function when needed
suspend fun loadImage(name: String): Image { … }
suspend fun loadAndCombine(name1: String, name2: String): Image {
val deferred1 = async { loadImage(name1) }
val deferred2 = async { loadImage(name2) }
return combineImages(deferred1.await(), deferred2.await())
}
Using async function when needed
suspend fun loadImage(name: String): Image { … }
suspend fun loadAndCombine(name1: String, name2: String): Image {
val deferred1 = async { loadImage(name1) }
val deferred2 = async { loadImage(name2) }
return combineImages(deferred1.await(), deferred2.await())
}
Kotlin approach to async
requestToken() VALID –> produces Token
async { requestToken() } VALID –> produces Deferred<Token>
sequential behavior
concurrent behavior
Kotlin
Kotlin
default
Coroutines
What are coroutines
conceptually?
What are coroutines
conceptually?
Coroutines are like very light-weight threads
fun main(args: Array<String>) = runBlocking<Unit> {
val jobs = List(100_000) {
launch {
delay(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
Example
fun main(args: Array<String>) = runBlocking<Unit> {
val jobs = List(100_000) {
launch {
delay(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
Example
This coroutine builder runs coroutine
in the context of invoker thread
fun main(args: Array<String>) = runBlocking<Unit> {
val jobs = List(100_000) {
launch {
delay(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
Example
fun main(args: Array<String>) = runBlocking<Unit> {
val jobs = List(100_000) {
launch {
delay(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
Example
fun main(args: Array<String>) = runBlocking<Unit> {
val jobs = List(100_000) {
launch {
delay(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
Example
Suspends for 1 second
fun main(args: Array<String>) = runBlocking<Unit> {
val jobs = List(100_000) {
launch {
delay(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
Example
We can join a job
just like a thread
Demo
fun main(args: Array<String>) = runBlocking<Unit> {
val jobs = List(100_000) {
launch {
delay(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
Example
Try that with 100k threads!
Prints 100k dots after one second delay
fun main(args: Array<String>) = runBlocking<Unit> {
val jobs = List(100_000) {
launch {
delay(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
Example
fun main(args: Array<String>) {
val jobs = List(100_000) {
thread {
Thread.sleep(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
Example
Demo
fun main(args: Array<String>) {
val jobs = List(100_000) {
thread {
Thread.sleep(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
Example
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
Java interop
CompletableFuture<Image> loadImageAsync(String name) { … }Java
CompletableFuture<Image> loadImageAsync(String name) { … }
CompletableFuture<Image> loadAndCombineAsync(String name1,
String name2)
Imagine implementing it in Java…
Java
CompletableFuture<Image> loadImageAsync(String name) { … }
CompletableFuture<Image> loadAndCombineAsync(String name1,
String name2)
{
CompletableFuture<Image> future1 = loadImageAsync(name1);
CompletableFuture<Image> future2 = loadImageAsync(name2);
return future1.thenCompose(image1 ->
future2.thenCompose(image2 ->
CompletableFuture.supplyAsync(() ->
combineImages(image1, image2))));
}
Java
CompletableFuture<Image> loadImageAsync(String name) { … }
CompletableFuture<Image> loadAndCombineAsync(String name1,
String name2)
{
CompletableFuture<Image> future1 = loadImageAsync(name1);
CompletableFuture<Image> future2 = loadImageAsync(name2);
return future1.thenCompose(image1 ->
future2.thenCompose(image2 ->
CompletableFuture.supplyAsync(() ->
combineImages(image1, image2))));
}
Java
CompletableFuture<Image> loadImageAsync(String name) { … }
fun loadAndCombineAsync(
name1: String,
name2: String
): CompletableFuture<Image> =
future {
val future1 = loadImageAsync(name1)
val future2 = loadImageAsync(name2)
combineImages(future1.await(), future2.await())
}
Kotlin
Java
CompletableFuture<Image> loadImageAsync(String name) { … }
fun loadAndCombineAsync(
name1: String,
name2: String
): CompletableFuture<Image> =
future {
val future1 = loadImageAsync(name1)
val future2 = loadImageAsync(name2)
combineImages(future1.await(), future2.await())
}
Kotlin
Java
CompletableFuture<Image> loadImageAsync(String name) { … }
fun loadAndCombineAsync(
name1: String,
name2: String
): CompletableFuture<Image> =
future {
val future1 = loadImageAsync(name1)
val future2 = loadImageAsync(name2)
combineImages(future1.await(), future2.await())
}
future coroutine builder
Kotlin
Java
CompletableFuture<Image> loadImageAsync(String name) { … }
fun loadAndCombineAsync(
name1: String,
name2: String
): CompletableFuture<Image> =
future {
val future1 = loadImageAsync(name1)
val future2 = loadImageAsync(name2)
combineImages(future1.await(), future2.await())
}
Kotlin
Java
CompletableFuture<Image> loadImageAsync(String name) { … }
fun loadAndCombineAsync(
name1: String,
name2: String
): CompletableFuture<Image> =
future {
val future1 = loadImageAsync(name1)
val future2 = loadImageAsync(name2)
combineImages(future1.await(), future2.await())
}
Extension for Java’s CompletableFuture
Kotlin
Java
Beyond
asynchronous code
Fibonacci sequence
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
A coroutine builder with
restricted suspension
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
A suspending function
The same building blocks
fun <T> buildSequence(
builderAction: suspend SequenceBuilder<T>.() -> Unit
): Sequence<T> { … }
fun <T> buildSequence(
builderAction: suspend SequenceBuilder<T>.() -> Unit
): Sequence<T> { … }
Result is a synchronous sequence
fun <T> buildSequence(
builderAction: suspend SequenceBuilder<T>.() -> Unit
): Sequence<T> { … }
Suspending lambda with receiver
fun <T> buildSequence(
builderAction: suspend SequenceBuilder<T>.() -> Unit
): Sequence<T> { … }
@RestrictsSuspension
abstract class SequenceBuilder<in T> {
abstract suspend fun yield(value: T)
}
Coroutine is restricted only to
suspending functions defined here
Synchronous
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
val iter = fibonacci.iterator()
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
val iter = fibonacci.iterator()
println(iter.next())
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
val iter = fibonacci.iterator()
println(iter.next())
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
val iter = fibonacci.iterator()
println(iter.next()) // 1
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
val iter = fibonacci.iterator()
println(iter.next()) // 1
println(iter.next())
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
val iter = fibonacci.iterator()
println(iter.next()) // 1
println(iter.next())
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
val iter = fibonacci.iterator()
println(iter.next()) // 1
println(iter.next()) // 1
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
val iter = fibonacci.iterator()
println(iter.next()) // 1
println(iter.next()) // 1
println(iter.next()) // 2
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(cur)
val tmp = cur + next
cur = next
next = tmp
}
}
val iter = fibonacci.iterator()
println(iter.next()) // 1
println(iter.next()) // 1
println(iter.next()) // 2
Synchronous with invoker
Library vs Language
Classic async
async/await
generate/yield
Keywords
Kotlin coroutines
suspend Modifier
Kotlin coroutines
Standard
library
Kotlin coroutines
Standard
library
kotlinx-coroutines
launch, async,
runBlocking, future, delay,
Job, Deferred, etc
http://github.com/kotlin/kotlinx.coroutines
Experimental status
Coroutines are here to stay
Backwards compatible inside 1.1 & 1.2
To be finalized in the future
#kotlinconf17
relizarov
elizarov at JetBrains
Roman Elizarov
Thank you
Any questions?

More Related Content

What's hot (20)

PDF
Kotlin Coroutines in Practice @ KotlinConf 2018
Roman Elizarov
 
PDF
Kotlin Coroutines Reloaded
Roman Elizarov
 
PPTX
JavaScript Event Loop
Designveloper
 
PDF
Coroutines in Kotlin. In-depth review
Dmytro Zaitsev
 
PPTX
Overlapped IO와 IOCP 조사 발표
Kwen Won Lee
 
PPT
PHP - Introduction to PHP Cookies and Sessions
Vibrant Technologies & Computers
 
PDF
PUC SE Day 2019 - SpringBoot
Josué Neis
 
PDF
Best Practices in Qt Quick/QML - Part IV
ICS
 
PDF
Pipeline oriented programming
Scott Wlaschin
 
PPTX
Coroutines in Kotlin
Alexey Soshin
 
PDF
Domain Modeling Made Functional (DevTernity 2022)
Scott Wlaschin
 
PDF
Windows Registered I/O (RIO) vs IOCP
Seungmo Koo
 
PDF
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
흥배 최
 
PDF
Jetpack compose
LutasLin
 
PDF
Railway Oriented Programming
Scott Wlaschin
 
PPT
The Kotlin Programming Language
intelliyole
 
PPT
Advanced Javascript
Adieu
 
PDF
JUnit & Mockito, first steps
Renato Primavera
 
PDF
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)
내훈 정
 
PDF
Introduction to RxJS
Brainhub
 
Kotlin Coroutines in Practice @ KotlinConf 2018
Roman Elizarov
 
Kotlin Coroutines Reloaded
Roman Elizarov
 
JavaScript Event Loop
Designveloper
 
Coroutines in Kotlin. In-depth review
Dmytro Zaitsev
 
Overlapped IO와 IOCP 조사 발표
Kwen Won Lee
 
PHP - Introduction to PHP Cookies and Sessions
Vibrant Technologies & Computers
 
PUC SE Day 2019 - SpringBoot
Josué Neis
 
Best Practices in Qt Quick/QML - Part IV
ICS
 
Pipeline oriented programming
Scott Wlaschin
 
Coroutines in Kotlin
Alexey Soshin
 
Domain Modeling Made Functional (DevTernity 2022)
Scott Wlaschin
 
Windows Registered I/O (RIO) vs IOCP
Seungmo Koo
 
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
흥배 최
 
Jetpack compose
LutasLin
 
Railway Oriented Programming
Scott Wlaschin
 
The Kotlin Programming Language
intelliyole
 
Advanced Javascript
Adieu
 
JUnit & Mockito, first steps
Renato Primavera
 
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)
내훈 정
 
Introduction to RxJS
Brainhub
 

Similar to Introduction to Coroutines @ KotlinConf 2017 (20)

PDF
Fresh Async with Kotlin @ QConSF 2017
Roman Elizarov
 
PDF
Fresh Async with Kotlin
C4Media
 
PDF
Coroutines in Kotlin. UA Mobile 2017.
UA Mobile
 
PDF
Dive into kotlins coroutines
Freddie Wang
 
PDF
Kotlin coroutine - behind the scenes
Anh Vu
 
PDF
Should it be routine to use coroutines?
Ion Stefan Brosteanu
 
PDF
Coroutines in Kotlin
Dmytro Zaitsev
 
PDF
Quick Introduction to Kotlin Coroutine for Android Dev
Shuhei Shogen
 
PPTX
2019-01-29 - Demystifying Kotlin Coroutines
Eamonn Boyle
 
PDF
Kotlin - Coroutine
Sean Tsai
 
PPTX
Coroutines talk ppt
Shahroz Khan
 
PDF
Structured concurrency with Kotlin Coroutines
Vadims Savjolovs
 
PPTX
Kotlin coroutines and spring framework
Sunghyouk Bae
 
PDF
Tackling Asynchrony with Kotlin Coroutines
Tech Triveni
 
PDF
Kotlin coroutine - the next step for RxJava developer?
Artur Latoszewski
 
PDF
Coroutines
Sevil Güler
 
PDF
JVMLS 2016. Coroutines in Kotlin
Andrey Breslav
 
PDF
Dip into Coroutines - KTUG Munich 202303
Alex Semin
 
PDF
DroidConEgypt-21-10-2022-Coroutines-AhmedNabil.pdf
Ahmed Nabil
 
PDF
Current State of Coroutines
Guido Pio Mariotti
 
Fresh Async with Kotlin @ QConSF 2017
Roman Elizarov
 
Fresh Async with Kotlin
C4Media
 
Coroutines in Kotlin. UA Mobile 2017.
UA Mobile
 
Dive into kotlins coroutines
Freddie Wang
 
Kotlin coroutine - behind the scenes
Anh Vu
 
Should it be routine to use coroutines?
Ion Stefan Brosteanu
 
Coroutines in Kotlin
Dmytro Zaitsev
 
Quick Introduction to Kotlin Coroutine for Android Dev
Shuhei Shogen
 
2019-01-29 - Demystifying Kotlin Coroutines
Eamonn Boyle
 
Kotlin - Coroutine
Sean Tsai
 
Coroutines talk ppt
Shahroz Khan
 
Structured concurrency with Kotlin Coroutines
Vadims Savjolovs
 
Kotlin coroutines and spring framework
Sunghyouk Bae
 
Tackling Asynchrony with Kotlin Coroutines
Tech Triveni
 
Kotlin coroutine - the next step for RxJava developer?
Artur Latoszewski
 
Coroutines
Sevil Güler
 
JVMLS 2016. Coroutines in Kotlin
Andrey Breslav
 
Dip into Coroutines - KTUG Munich 202303
Alex Semin
 
DroidConEgypt-21-10-2022-Coroutines-AhmedNabil.pdf
Ahmed Nabil
 
Current State of Coroutines
Guido Pio Mariotti
 
Ad

More from Roman Elizarov (18)

PDF
Scale Up with Lock-Free Algorithms @ JavaOne
Roman Elizarov
 
PDF
Lock-free algorithms for Kotlin Coroutines
Roman Elizarov
 
PPTX
Non blocking programming and waiting
Roman Elizarov
 
PDF
ACM ICPC 2016 NEERC (Northeastern European Regional Contest) Problems Review
Roman Elizarov
 
PDF
Многопоточное Программирование - Теория и Практика
Roman Elizarov
 
PDF
Wait for your fortune without Blocking!
Roman Elizarov
 
PDF
ACM ICPC 2015 NEERC (Northeastern European Regional Contest) Problems Review
Roman Elizarov
 
PDF
ACM ICPC 2014 NEERC (Northeastern European Regional Contest) Problems Review
Roman Elizarov
 
PDF
Why GC is eating all my CPU?
Roman Elizarov
 
PDF
Многопоточные Алгоритмы (для BitByte 2014)
Roman Elizarov
 
PDF
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)
Roman Elizarov
 
PPTX
DIY Java Profiling
Roman Elizarov
 
PDF
ACM ICPC 2013 NEERC (Northeastern European Regional Contest) Problems Review
Roman Elizarov
 
PPTX
Java Serialization Facts and Fallacies
Roman Elizarov
 
PPTX
Millions quotes per second in pure java
Roman Elizarov
 
PPTX
ACM ICPC 2012 NEERC (Northeastern European Regional Contest) Problems Review
Roman Elizarov
 
PPTX
The theory of concurrent programming for a seasoned programmer
Roman Elizarov
 
PPTX
Пишем самый быстрый хеш для кэширования данных
Roman Elizarov
 
Scale Up with Lock-Free Algorithms @ JavaOne
Roman Elizarov
 
Lock-free algorithms for Kotlin Coroutines
Roman Elizarov
 
Non blocking programming and waiting
Roman Elizarov
 
ACM ICPC 2016 NEERC (Northeastern European Regional Contest) Problems Review
Roman Elizarov
 
Многопоточное Программирование - Теория и Практика
Roman Elizarov
 
Wait for your fortune without Blocking!
Roman Elizarov
 
ACM ICPC 2015 NEERC (Northeastern European Regional Contest) Problems Review
Roman Elizarov
 
ACM ICPC 2014 NEERC (Northeastern European Regional Contest) Problems Review
Roman Elizarov
 
Why GC is eating all my CPU?
Roman Elizarov
 
Многопоточные Алгоритмы (для BitByte 2014)
Roman Elizarov
 
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)
Roman Elizarov
 
DIY Java Profiling
Roman Elizarov
 
ACM ICPC 2013 NEERC (Northeastern European Regional Contest) Problems Review
Roman Elizarov
 
Java Serialization Facts and Fallacies
Roman Elizarov
 
Millions quotes per second in pure java
Roman Elizarov
 
ACM ICPC 2012 NEERC (Northeastern European Regional Contest) Problems Review
Roman Elizarov
 
The theory of concurrent programming for a seasoned programmer
Roman Elizarov
 
Пишем самый быстрый хеш для кэширования данных
Roman Elizarov
 
Ad

Recently uploaded (20)

PDF
UiPath DevConnect 2025: Agentic Automation Community User Group Meeting
DianaGray10
 
PPTX
Agentforce World Tour Toronto '25 - MCP with MuleSoft
Alexandra N. Martinez
 
PDF
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
PDF
“Computer Vision at Sea: Automated Fish Tracking for Sustainable Fishing,” a ...
Edge AI and Vision Alliance
 
DOCX
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
PDF
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
PDF
Transcript: Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
PPTX
MuleSoft MCP Support (Model Context Protocol) and Use Case Demo
shyamraj55
 
PDF
How do you fast track Agentic automation use cases discovery?
DianaGray10
 
PDF
Future-Proof or Fall Behind? 10 Tech Trends You Can’t Afford to Ignore in 2025
DIGITALCONFEX
 
PPTX
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
PDF
What’s my job again? Slides from Mark Simos talk at 2025 Tampa BSides
Mark Simos
 
PDF
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
PDF
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 
PDF
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
DOCX
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
PDF
Staying Human in a Machine- Accelerated World
Catalin Jora
 
PDF
Automating Feature Enrichment and Station Creation in Natural Gas Utility Net...
Safe Software
 
PPTX
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
PPTX
Mastering ODC + Okta Configuration - Chennai OSUG
HathiMaryA
 
UiPath DevConnect 2025: Agentic Automation Community User Group Meeting
DianaGray10
 
Agentforce World Tour Toronto '25 - MCP with MuleSoft
Alexandra N. Martinez
 
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
“Computer Vision at Sea: Automated Fish Tracking for Sustainable Fishing,” a ...
Edge AI and Vision Alliance
 
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
Transcript: Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
MuleSoft MCP Support (Model Context Protocol) and Use Case Demo
shyamraj55
 
How do you fast track Agentic automation use cases discovery?
DianaGray10
 
Future-Proof or Fall Behind? 10 Tech Trends You Can’t Afford to Ignore in 2025
DIGITALCONFEX
 
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
What’s my job again? Slides from Mark Simos talk at 2025 Tampa BSides
Mark Simos
 
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
Staying Human in a Machine- Accelerated World
Catalin Jora
 
Automating Feature Enrichment and Station Creation in Natural Gas Utility Net...
Safe Software
 
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
Mastering ODC + Okta Configuration - Chennai OSUG
HathiMaryA
 

Introduction to Coroutines @ KotlinConf 2017

  • 1. elizarov at JetBrains Roman Elizarov Introduction to Coroutines
  • 3. How do we write code that waits for something most of the time?
  • 4. A toy problem Kotlin fun requestToken(): Token { // makes request for a token & waits return token // returns result when received } 1
  • 5. fun requestToken(): Token { … } fun createPost(token: Token, item: Item): Post { // sends item to the server & waits return post // returns resulting post } A toy problem 2 Kotlin
  • 6. fun requestToken(): Token { … } fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { // does some local processing of result } A toy problem 3 Kotlin
  • 7. fun requestToken(): Token { … } fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } A toy problem fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } 1 2 3 Can be done with threads! Kotlin
  • 8. fun requestToken(): Token { // makes request for a token // blocks the thread waiting for result return token // returns result when received } fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } Threads fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Is anything wrong with it?
  • 9. How many threads we can have? 100 🙂
  • 10. How many threads we can have? 1000 😅
  • 11. How many threads we can have? 10 000 😩
  • 12. How many threads we can have? 100 000 😵
  • 13. Callbacks to the rescue Sort of …
  • 14. Callbacks: before fun requestToken(): Token { // makes request for a token & waits return token // returns result when received } 1
  • 15. Callbacks: after fun requestTokenAsync(cb: (Token) -> Unit) { // makes request for a token, invokes callback when done // returns immediately } 1 callback
  • 16. Callbacks: before fun requestTokenAsync(cb: (Token) -> Unit) { … } fun createPost(token: Token, item: Item): Post { // sends item to the server & waits return post // returns resulting post } 2
  • 17. Callbacks: after fun requestTokenAsync(cb: (Token) -> Unit) { … } fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { // sends item to the server, invokes callback when done // returns immediately } 2 callback
  • 18. Callbacks: before fun requestTokenAsync(cb: (Token) -> Unit) { … } fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }
  • 19. Callbacks: after fun requestTokenAsync(cb: (Token) -> Unit) { … } fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { … } fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync { token -> createPostAsync(token, item) { post -> processPost(post) } } } aka “callback hell” This is simplified. Handling exceptions makes it a real mess
  • 21. Futures: before fun requestTokenAsync(cb: (Token) -> Unit) { // makes request for a token, invokes callback when done // returns immediately } 1
  • 22. Futures: after fun requestTokenAsync(): Promise<Token> { // makes request for a token // returns promise for a future result immediately } 1 future
  • 23. Futures: before fun requestTokenAsync(): Promise<Token> { … } fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { // sends item to the server, invokes callback when done // returns immediately } 2
  • 24. Futures: after fun requestTokenAsync(): Promise<Token> { … } fun createPostAsync(token: Token, item: Item): Promise<Post> { // sends item to the server // returns promise for a future result immediately } future 2
  • 25. Futures: before fun requestTokenAsync(): Promise<Token> { … } fun createPostAsync(token: Token, item: Item): Promise<Post> … fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync { token -> createPostAsync(token, item) { post -> processPost(post) } } }
  • 26. Futures: after fun requestTokenAsync(): Promise<Token> { … } fun createPostAsync(token: Token, item: Item): Promise<Post> … fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync() .thenCompose { token -> createPostAsync(token, item) } .thenAccept { post -> processPost(post) } } Composable & propagates exceptions No nesting indentation
  • 27. Futures: after fun requestTokenAsync(): Promise<Token> { … } fun createPostAsync(token: Token, item: Item): Promise<Post> … fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync() .thenCompose { token -> createPostAsync(token, item) } .thenAccept { post -> processPost(post) } } But all those combinators…
  • 28. Kotlin coroutines to the rescue Let’s get real
  • 29. Coroutines: before fun requestTokenAsync(): Promise<Token> { // makes request for a token // returns promise for a future result immediately } 1
  • 30. Coroutines: after suspend fun requestToken(): Token { // makes request for a token & suspends return token // returns result when received } 1
  • 31. Coroutines: after suspend fun requestToken(): Token { // makes request for a token & suspends return token // returns result when received } 1 natural signature
  • 32. Coroutines: before suspend fun requestToken(): Token { … } fun createPostAsync(token: Token, item: Item): Promise<Post> { // sends item to the server // returns promise for a future result immediately } 2
  • 33. Coroutines: after suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { // sends item to the server & suspends return post // returns result when received } 2
  • 34. Coroutines: before suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync() .thenCompose { token -> createPostAsync(token, item) } .thenAccept { post -> processPost(post) } }
  • 35. Coroutines: after suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }
  • 36. Coroutines: after suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Like regular code
  • 37. Coroutines: after suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } suspension points
  • 38. • Regular loops Bonus features for ((token, item) in list) { createPost(token, item) }
  • 39. • Regular exception handing Bonus features try { createPost(token, item) } catch (e: BadTokenException) { … }
  • 40. • Regular higher-order functions • forEach, let, apply, repeat, filter, map, use, etc Bonus features file.readLines().forEach { line -> createPost(token, line.toItem()) } Everything like in blocking code
  • 42. Retrofit async interface Service { fun createPost(token: Token, item: Item): Call<Post> }
  • 43. Retrofit async interface Service { fun createPost(token: Token, item: Item): Call<Post> } future
  • 44. Retrofit async interface Service { fun createPost(token: Token, item: Item): Call<Post> } suspend fun createPost(token: Token, item: Item): Post = serviceInstance.createPost(token, item).await()
  • 45. Retrofit async interface Service { fun createPost(token: Token, item: Item): Call<Post> } suspend fun createPost(token: Token, item: Item): Post = serviceInstance.createPost(token, item).await() natural signature
  • 46. Retrofit async interface Service { fun createPost(token: Token, item: Item): Call<Post> } suspend fun createPost(token: Token, item: Item): Post = serviceInstance.createPost(token, item).await() Suspending extension function from integration library
  • 48. val post = createPost(token, item)
  • 49. Higher-order functions val post = retryIO { createPost(token, item) }
  • 50. Higher-order functions val post = retryIO { createPost(token, item) } suspend fun <T> retryIO(block: suspend () -> T): T { var curDelay = 1000L // start with 1 sec while (true) { try { return block() } catch (e: IOException) { e.printStackTrace() // log the error } delay(curDelay) curDelay = (curDelay * 2).coerceAtMost(60000L) } }
  • 51. Higher-order functions val post = retryIO { createPost(token, item) } suspend fun <T> retryIO(block: suspend () -> T): T { var curDelay = 1000L // start with 1 sec while (true) { try { return block() } catch (e: IOException) { e.printStackTrace() // log the error } delay(curDelay) curDelay = (curDelay * 2).coerceAtMost(60000L) } }
  • 52. Higher-order functions val post = retryIO { createPost(token, item) } suspend fun <T> retryIO(block: suspend () -> T): T { var curDelay = 1000L // start with 1 sec while (true) { try { return block() } catch (e: IOException) { e.printStackTrace() // log the error } delay(curDelay) curDelay = (curDelay * 2).coerceAtMost(60000L) } } suspending lambda
  • 53. Higher-order functions val post = retryIO { createPost(token, item) } suspend fun <T> retryIO(block: suspend () -> T): T { var curDelay = 1000L // start with 1 sec while (true) { try { return block() } catch (e: IOException) { e.printStackTrace() // log the error } delay(curDelay) curDelay = (curDelay * 2).coerceAtMost(60000L) } } Everything like in blocking code
  • 54. Higher-order functions val post = retryIO { createPost(token, item) } suspend fun <T> retryIO(block: suspend () -> T): T { var curDelay = 1000L // start with 1 sec while (true) { try { return block() } catch (e: IOException) { e.printStackTrace() // log the error } delay(curDelay) curDelay = (curDelay * 2).coerceAtMost(60000L) } }
  • 56. Coroutines revisited suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }
  • 57. Coroutines revisited suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }
  • 58. Coroutines revisited suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Error: Suspend function 'requestToken' should be called only from a coroutine or another suspend function
  • 59. Coroutines revisited suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Can suspend execution
  • 60. Coroutines revisited suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Can suspend executionA regular function cannot
  • 61. Coroutines revisited suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Can suspend executionA regular function cannot One cannot simply invoke a suspending function
  • 62. Launch fun postItem(item: Item) { launch { val token = requestToken() val post = createPost(token, item) processPost(post) } } coroutine builder
  • 63. fun postItem(item: Item) { launch { val token = requestToken() val post = createPost(token, item) processPost(post) } } Fire and forget! Returns immediately, coroutine works in background thread pool
  • 64. fun postItem(item: Item) { launch { val token = requestToken() val post = createPost(token, item) processPost(post) } }
  • 65. fun postItem(item: Item) { launch(UI) { val token = requestToken() val post = createPost(token, item) processPost(post) } } UI Context Just specify the context
  • 66. fun postItem(item: Item) { launch(UI) { val token = requestToken() val post = createPost(token, item) processPost(post) } } UI Context And it gets executed on UI thread
  • 67. Where’s the magic of launch?
  • 68. fun launch( context: CoroutineContext = DefaultDispatcher, block: suspend () -> Unit ): Job { … } A regular function
  • 69. fun launch( context: CoroutineContext = DefaultDispatcher, block: suspend () -> Unit ): Job { … } suspending lambda
  • 70. fun launch( context: CoroutineContext = DefaultDispatcher, block: suspend () -> Unit ): Job { … }
  • 72. Kotlin-way suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Kotlin suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … }
  • 73. async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post); } Classic-way C# approach to the same problem (also Python, TS, Dart, coming to JS) C# async Task<Token> requestToken() { … } async Task<Post> createPost(Token token, Item item) { … } void processPost(Post post) { … }
  • 74. async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post); } Classic-way mark with async C# async Task<Token> requestToken() { … } async Task<Post> createPost(Token token, Item item) { … } void processPost(Post post) { … }
  • 75. async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post); } Classic-way use await to suspend C# async Task<Token> requestToken() { … } async Task<Post> createPost(Token token, Item item) { … } void processPost(Post post) { … }
  • 76. async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post); } Classic-way C# returns a future async Task<Token> requestToken() { … } async Task<Post> createPost(Token token, Item item) { … } void processPost(Post post) { … }
  • 77. Why no await keyword in Kotlin? The problem with async requestToken() VALID –> produces Task<Token> await requestToken() VALID –> produces Token concurrent behavior sequential behavior C# C# default
  • 78. Kotlin suspending functions are designed to imitate sequential behavior by default Concurrency is hard Concurrency has to be explicit
  • 79. Kotlin approach to async Concurrency where you need it
  • 80. Use-case for async async Task<Image> loadImageAsync(String name) { … }C#
  • 81. Use-case for async var promise1 = loadImageAsync(name1); var promise2 = loadImageAsync(name2); async Task<Image> loadImageAsync(String name) { … } Start multiple operations concurrently C#
  • 82. Use-case for async var promise1 = loadImageAsync(name1); var promise2 = loadImageAsync(name2); var image1 = await promise1; var image2 = await promise2; async Task<Image> loadImageAsync(String name) { … } and then wait for them C#
  • 83. Use-case for async var result = combineImages(image1, image2); C# var promise1 = loadImageAsync(name1); var promise2 = loadImageAsync(name2); var image1 = await promise1; var image2 = await promise2; async Task<Image> loadImageAsync(String name) { … }
  • 84. Kotlin async function fun loadImageAsync(name: String): Deferred<Image> = async { … } Kotlin
  • 85. Kotlin async function fun loadImageAsync(name: String): Deferred<Image> = async { … } Kotlin A regular function
  • 86. Kotlin async function fun loadImageAsync(name: String): Deferred<Image> = async { … } Kotlin’s future type Kotlin
  • 87. Kotlin async function fun loadImageAsync(name: String): Deferred<Image> = async { … } async coroutine builder Kotlin
  • 88. Kotlin async function fun loadImageAsync(name: String): Deferred<Image> = async { … } val deferred1 = loadImageAsync(name1) val deferred2 = loadImageAsync(name2) Start multiple operations concurrently Kotlin
  • 89. Kotlin async function fun loadImageAsync(name: String): Deferred<Image> = async { … } val deferred1 = loadImageAsync(name1) val deferred2 = loadImageAsync(name2) val image1 = deferred1.await() val image2 = deferred2.await() and then wait for them await function Suspends until deferred is complete Kotlin
  • 90. Kotlin async function fun loadImageAsync(name: String): Deferred<Image> = async { … } val deferred1 = loadImageAsync(name1) val deferred2 = loadImageAsync(name2) val image1 = deferred1.await() val image2 = deferred2.await() val result = combineImages(image1, image2) Kotlin
  • 91. Using async function when needed suspend fun loadImage(name: String): Image { … } Is defined as suspending function, not async
  • 92. Using async function when needed suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } return combineImages(deferred1.await(), deferred2.await()) }
  • 93. Using async function when needed suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } return combineImages(deferred1.await(), deferred2.await()) }
  • 94. Using async function when needed suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } return combineImages(deferred1.await(), deferred2.await()) }
  • 95. Using async function when needed suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } return combineImages(deferred1.await(), deferred2.await()) }
  • 96. Kotlin approach to async requestToken() VALID –> produces Token async { requestToken() } VALID –> produces Deferred<Token> sequential behavior concurrent behavior Kotlin Kotlin default
  • 99. What are coroutines conceptually? Coroutines are like very light-weight threads
  • 100. fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } } Example
  • 101. fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } } Example This coroutine builder runs coroutine in the context of invoker thread
  • 102. fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } } Example
  • 103. fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } } Example
  • 104. fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } } Example Suspends for 1 second
  • 105. fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } } Example We can join a job just like a thread
  • 106. Demo
  • 107. fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } } Example Try that with 100k threads! Prints 100k dots after one second delay
  • 108. fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } } Example
  • 109. fun main(args: Array<String>) { val jobs = List(100_000) { thread { Thread.sleep(1000L) print(".") } } jobs.forEach { it.join() } } Example
  • 110. Demo
  • 111. fun main(args: Array<String>) { val jobs = List(100_000) { thread { Thread.sleep(1000L) print(".") } } jobs.forEach { it.join() } } Example Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
  • 114. CompletableFuture<Image> loadImageAsync(String name) { … } CompletableFuture<Image> loadAndCombineAsync(String name1, String name2) Imagine implementing it in Java… Java
  • 115. CompletableFuture<Image> loadImageAsync(String name) { … } CompletableFuture<Image> loadAndCombineAsync(String name1, String name2) { CompletableFuture<Image> future1 = loadImageAsync(name1); CompletableFuture<Image> future2 = loadImageAsync(name2); return future1.thenCompose(image1 -> future2.thenCompose(image2 -> CompletableFuture.supplyAsync(() -> combineImages(image1, image2)))); } Java
  • 116. CompletableFuture<Image> loadImageAsync(String name) { … } CompletableFuture<Image> loadAndCombineAsync(String name1, String name2) { CompletableFuture<Image> future1 = loadImageAsync(name1); CompletableFuture<Image> future2 = loadImageAsync(name2); return future1.thenCompose(image1 -> future2.thenCompose(image2 -> CompletableFuture.supplyAsync(() -> combineImages(image1, image2)))); } Java
  • 117. CompletableFuture<Image> loadImageAsync(String name) { … } fun loadAndCombineAsync( name1: String, name2: String ): CompletableFuture<Image> = future { val future1 = loadImageAsync(name1) val future2 = loadImageAsync(name2) combineImages(future1.await(), future2.await()) } Kotlin Java
  • 118. CompletableFuture<Image> loadImageAsync(String name) { … } fun loadAndCombineAsync( name1: String, name2: String ): CompletableFuture<Image> = future { val future1 = loadImageAsync(name1) val future2 = loadImageAsync(name2) combineImages(future1.await(), future2.await()) } Kotlin Java
  • 119. CompletableFuture<Image> loadImageAsync(String name) { … } fun loadAndCombineAsync( name1: String, name2: String ): CompletableFuture<Image> = future { val future1 = loadImageAsync(name1) val future2 = loadImageAsync(name2) combineImages(future1.await(), future2.await()) } future coroutine builder Kotlin Java
  • 120. CompletableFuture<Image> loadImageAsync(String name) { … } fun loadAndCombineAsync( name1: String, name2: String ): CompletableFuture<Image> = future { val future1 = loadImageAsync(name1) val future2 = loadImageAsync(name2) combineImages(future1.await(), future2.await()) } Kotlin Java
  • 121. CompletableFuture<Image> loadImageAsync(String name) { … } fun loadAndCombineAsync( name1: String, name2: String ): CompletableFuture<Image> = future { val future1 = loadImageAsync(name1) val future2 = loadImageAsync(name2) combineImages(future1.await(), future2.await()) } Extension for Java’s CompletableFuture Kotlin Java
  • 123. Fibonacci sequence val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } }
  • 124. val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } A coroutine builder with restricted suspension
  • 125. val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } A suspending function
  • 126. The same building blocks fun <T> buildSequence( builderAction: suspend SequenceBuilder<T>.() -> Unit ): Sequence<T> { … }
  • 127. fun <T> buildSequence( builderAction: suspend SequenceBuilder<T>.() -> Unit ): Sequence<T> { … } Result is a synchronous sequence
  • 128. fun <T> buildSequence( builderAction: suspend SequenceBuilder<T>.() -> Unit ): Sequence<T> { … } Suspending lambda with receiver
  • 129. fun <T> buildSequence( builderAction: suspend SequenceBuilder<T>.() -> Unit ): Sequence<T> { … } @RestrictsSuspension abstract class SequenceBuilder<in T> { abstract suspend fun yield(value: T) } Coroutine is restricted only to suspending functions defined here
  • 130. Synchronous val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator()
  • 131. val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next())
  • 132. val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next())
  • 133. val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1
  • 134. val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1 println(iter.next())
  • 135. val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1 println(iter.next())
  • 136. val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1 println(iter.next()) // 1
  • 137. val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1 println(iter.next()) // 1 println(iter.next()) // 2
  • 138. val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1 println(iter.next()) // 1 println(iter.next()) // 2 Synchronous with invoker
  • 143. Kotlin coroutines Standard library kotlinx-coroutines launch, async, runBlocking, future, delay, Job, Deferred, etc http://github.com/kotlin/kotlinx.coroutines
  • 144. Experimental status Coroutines are here to stay Backwards compatible inside 1.1 & 1.2 To be finalized in the future
  • 145. #kotlinconf17 relizarov elizarov at JetBrains Roman Elizarov Thank you Any questions?