v4: Coroutines Direct Style
suspend fun postItem(item: Item) {
val token = prepareToken() /
/ 1
val post = submitPost(token, item) /
/ 2
processPost(post) /
/ 3
}
Slide 9
Slide 9 text
v4: Coroutines Direct Style
suspend fun postItem(item: Item) {
val token = prepareToken() /
/ 1
val post = submitPost(token, item) /
/ 2
processPost(post) /
/ 3
}
Slide 10
Slide 10 text
v4: Coroutines Direct Style
suspend fun postItem(item: Item) {
val token = prepareToken() /
/ 1
val post = submitPost(token, item) /
/ 2
processPost(post) /
/ 3
}
Slide 11
Slide 11 text
Profit: try/catch, loops, std-lib
suspend fun postItems(items: List) {
try {
val token = prepareToken()
items.forEach { item ->
val post = submitPost(token, item)
processPost(post)
}
} catch (e: BadTokenException) { /*…*/ }
}
Slide 12
Slide 12 text
suspend fun
computation that can be suspended
Slide 13
Slide 13 text
Experimental status
Since Kotlin 1.1
-Xcoroutines=enable
kotlin.coroutines.experimental -> kotlin.coroutines (1.3)
Experimental != unstable
Can and should be used in production
Slide 14
Slide 14 text
Experimental status
New style of programming
The design is not final and expected to change
JetBrains still collects information and feedbacks
Backwards compatibility guaranteed
Slide 15
Slide 15 text
Terminology
coroutine
suspending function
suspending lambda
suspending function type
coroutine builder
suspension point
continuation
Slide 16
Slide 16 text
Terminology
coroutine
suspending function
suspending lambda
suspending function type
coroutine builder
suspension point
continuation
Slide 17
Slide 17 text
A coroutine is…
an instance of suspendable computation
similar to a daemon thread, but very light-weight
similar to a future or promise
Slide 18
Slide 18 text
Why coroutines?
threads are expensive to keep and switch
your code is single threaded
you’ve got lots of mutable states
Continuations
suspend fun postItem(item: Item) {
val token = prepareToken()
val post = submitPost(token, item)
processPost(post)
}
Initial continuation
Slide 26
Slide 26 text
Continuations
suspend fun postItem(item: Item) {
val token = prepareToken()
val post = submitPost(token, item)
processPost(post)
}
Continuation
Slide 27
Slide 27 text
Continuations
suspend fun postItem(item: Item) {
val token = prepareToken()
val post = submitPost(token, item)
processPost(post)
}
Continuation
Slide 28
Slide 28 text
Labels
suspend fun postItem(item: Item) {
/
/ LABEL 0
val token = prepareToken()
/
/ LABEL 1
val post = submitPost(token, item)
/
/ LABEL 2
processPost(post)
}
Slide 29
Slide 29 text
Labels
suspend fun postItem(item: Item) {
switch(label) {
case 0:
val token = prepareToken()
case 1:
val post = submitPost(token, item)
case 2:
processPost(post)
}
}
Slide 30
Slide 30 text
State
suspend fun postItem(item: Item) {
val stateMachine = object: CoroutineImpl {…}
switch(stateMachine.label) {
case 0:
val token = prepareToken()
case 1:
val post = submitPost(token, item)
case 2:
processPost(post)
}
}
Slide 31
Slide 31 text
CPS Transform
fun postItem(item: Item, cont: Continuation) {
val stateMachine = object: CoroutineImpl {…}
switch(stateMachine.label) {
case 0:
val token = prepareToken(stateMachine)
case 1:
val post = submitPost(token, item, stateMachine)
case 2:
processPost(post)
}
}
Slide 32
Slide 32 text
Save state
fun postItem(item: Item, cont: Continuation) {
val stateMachine = object: CoroutineImpl {…}
switch(stateMachine.label) {
case 0:
stateMachine.item = item
stateMachine.label = 1
prepareToken(stateMachine)
case 1:
…
}
}
Slide 33
Slide 33 text
Callback
fun postItem(item: Item, cont: Continuation) {
val stateMachine = cont as? ThisSM ?: object: ThisSM {
fun resume(…) {
postItem(null, this)
}
}
switch(stateMachine.label) {
case 0:
….
}
}
Slide 34
Slide 34 text
Restore state and continue
fun postItem(item: Item, cont: Continuation) {
…
case 0:
stateMachine.item = item
stateMachine.label = 1
prepareToken(stateMachine)
case 1:
val item = stateMachine.item
val token = stateMachine.result
stateMachine.label = 2
submitPost(token, item, stateMachine)
case 2:
…
Slide 35
Slide 35 text
How to create custom
coroutines?
Slide 36
Slide 36 text
Await for a Single
suspend fun Single.await(): T = TODO()
Slide 37
Slide 37 text
Suspend a coroutine
suspend fun Single.await(): T = suspendCancellableCoroutine { cont ->
/
/ …
}
Slide 38
Slide 38 text
Subscribe
suspend fun Single.await(): T = suspendCancellableCoroutine { cont ->
subscribe(object : SingleObserver {
override fun onSuccess(t: T) {
TODO()
}
override fun onError(error: Throwable) {
TODO()
}
override fun onSubscribe(d: Disposable) {
TODO()
}
})
}
Slide 39
Slide 39 text
Return a result, if successful
suspend fun Single.await(): T = suspendCancellableCoroutine { cont ->
subscribe(object : SingleObserver {
override fun onSuccess(t: T) {
cont.resume(t)
}
override fun onError(error: Throwable) {
TODO()
}
override fun onSubscribe(d: Disposable) {
TODO()
}
})
}
Slide 40
Slide 40 text
Or resume with exception
suspend fun Single.await(): T = suspendCancellableCoroutine { cont ->
subscribe(object : SingleObserver {
override fun onSuccess(t: T) {
cont.resume(t)
}
override fun onError(error: Throwable) {
cont.resumeWithException(error)
}
override fun onSubscribe(d: Disposable) {
TODO()
}
})
}
Slide 41
Slide 41 text
Don’t forget to dispose
suspend fun Single.await(): T = suspendCancellableCoroutine { cont ->
subscribe(object : SingleObserver {
override fun onSuccess(t: T) {
cont.resume(t)
}
override fun onError(error: Throwable) {
cont.resumeWithException(error)
}
override fun onSubscribe(d: Disposable) {
cont.invokeOnCompletion { d.dispose() }
}
})
}
Slide 42
Slide 42 text
And this is it
suspend fun Single.await(): T = suspendCancellableCoroutine { cont ->
subscribe(object : SingleObserver {
override fun onSuccess(t: T) {
cont.resume(t)
}
override fun onError(error: Throwable) {
cont.resumeWithException(error)
}
override fun onSubscribe(d: Disposable) {
cont.invokeOnCompletion { d.dispose() }
}
})
}
async/await
/
/ C# way
async Task ProcessImage(String url)
{
var image = await LoadImage(url);
imageCache.Add(image);
}
/
/ Kotlin way
suspend fun processImage(url: String) {
val image = loadImageAsync(url).await()
imageCache.add(image)
}
Slide 46
Slide 46 text
Not idiomatic way
fun loadImageAsync(url: String): Deferred = async { TODO() }
suspend fun processImage(url: String) {
val image = loadImageAsync(url).await()
imageCache.add(image)
}
Don’t define async functions
in the first place
Slide 47
Slide 47 text
Idiomatic way
suspend fun loadImage(url: String): Image = TODO()
suspend fun processImage(url: String) {
val image = async { loadImage(url) }.await()
imageCache.add(image)
}
Keep concurrency explicit
Slide 48
Slide 48 text
Generators API in
kotlin.coroutines
kotlin.coroutines.experimental
buildIterator()
buildSequence()
Slide 49
Slide 49 text
buildSequence {
print(“Start: ")
var prev = 1; var cur = 1
while (true) {
print(“Next")
yield(prev) /
/ suspension point
val next = prev + cur
prev = cur; cur = next
}
print("End") /
/ unreachable code
}.take(6).forEach { print(" $it ") }
/
/ Output: Start 1 Next 1 Next 2 Next 3 Next 5 Next 8
Slide 50
Slide 50 text
buildSequence {
print(“Start: ")
var prev = 1; var cur = 1
while (true) {
print(“Next")
yield(prev) /
/ suspension point
val next = prev + cur
prev = cur; cur = next
}
print("End") /
/ unreachable code
}.take(6).forEach { print(" $it ") }
/
/ Output: Start 1 Next 1 Next 2 Next 3 Next 5 Next 8
Slide 51
Slide 51 text
buildSequence {
print(“Start: ")
var prev = 1; var cur = 1
while (true) {
print(“Next")
yield(prev) /
/ suspension point
val next = prev + cur
prev = cur; cur = next
}
print("End") /
/ unreachable code
}.take(6).forEach { print(" $it ") }
/
/ Output: Start 1 Next 1 Next 2 Next 3 Next 5 Next 8
Slide 52
Slide 52 text
buildSequence {
print(“Start: ")
var prev = 1; var cur = 1
while (true) {
print(“Next")
yield(prev) /
/ suspension point
val next = prev + cur
prev = cur; cur = next
}
print("End") /
/ unreachable code
}.take(6).forEach { print(" $it ") }
/
/ Output: Start 1 Next 1 Next 2 Next 3 Next 5 Next 8
Slide 53
Slide 53 text
buildSequence {
print(“Start: ")
var prev = 1; var cur = 1
while (true) {
print(“Next")
yield(prev) /
/ suspension point
val next = prev + cur
prev = cur; cur = next
}
print("End") /
/ unreachable code
}.take(6).forEach { print(" $it ") }
/
/ Output: Start 1 Next 1 Next 2 Next 3 Next 5 Next 8
Slide 54
Slide 54 text
launch
fire and forget
(Job)
Slide 55
Slide 55 text
run,
runBlocking
for main functions
and tests
Slide 56
Slide 56 text
delay
like Thread.sleep(),
but non-blocking
Slide 57
Slide 57 text
Channel
transfers values
between coroutines
Slide 58
Slide 58 text
produce
produces a stream of values
by sending them to a channel
(ProducerJob)
Slide 59
Slide 59 text
actor
deals with it’s mailbox
(ActorJob)
Slide 60
Slide 60 text
select
waits for the result of
multiple suspending
functions
Slide 61
Slide 61 text
Job lifecycle
New Active
Cancelling
Completed
Cancelled
Slide 62
Slide 62 text
Job states
State isActive isCompleted isCancelled
New - - -
Active ✔ - -
Completed - ✔ -
Cancelling - - ✔
Cancelled - ✔ ✔