13. Experimental status
Since Kotlin 1.1
-Xcoroutines=enable
kotlin.coroutines.experimental -> kotlin.coroutines (1.3)
Experimental != unstable
Can and should be used in production
14. Experimental status
New style of programming
The design is not final and expected to change
JetBrains still collects information and feedbacks
Backwards compatibility guaranteed
20. Standard API
• Language support (`suspend` keyword)
• low-level basic API (stdlib: kotlin.coroutines)
• high-level APIs that can be used in user code
28. Labels
suspend fun postItem(item: Item) {
// LABEL 0
val token = prepareToken()
// LABEL 1
val post = submitPost(token, item)
// LABEL 2
processPost(post)
}
29. Labels
suspend fun postItem(item: Item) {
switch(label) {
case 0:
val token = prepareToken()
case 1:
val post = submitPost(token, item)
case 2:
processPost(post)
}
}
30. 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)
}
}
31. 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)
}
}
32. 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:
…
}
}
33. Callback
fun postItem(item: Item, cont: Continuation) {
val stateMachine = cont as? ThisSM ?: object: ThisSM {
fun resume(…) {
postItem(null, this)
}
}
switch(stateMachine.label) {
case 0:
….
}
}
34. 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:
…
38. Subscribe
suspend fun <T> Single<T>.await(): T = suspendCancellableCoroutine { cont ->
subscribe(object : SingleObserver<T> {
override fun onSuccess(t: T) {
TODO()
}
override fun onError(error: Throwable) {
TODO()
}
override fun onSubscribe(d: Disposable) {
TODO()
}
})
}
39. Return a result, if successful
suspend fun <T> Single<T>.await(): T = suspendCancellableCoroutine { cont ->
subscribe(object : SingleObserver<T> {
override fun onSuccess(t: T) {
cont.resume(t)
}
override fun onError(error: Throwable) {
TODO()
}
override fun onSubscribe(d: Disposable) {
TODO()
}
})
}
40. Or resume with exception
suspend fun <T> Single<T>.await(): T = suspendCancellableCoroutine { cont ->
subscribe(object : SingleObserver<T> {
override fun onSuccess(t: T) {
cont.resume(t)
}
override fun onError(error: Throwable) {
cont.resumeWithException(error)
}
override fun onSubscribe(d: Disposable) {
TODO()
}
})
}
41. Don’t forget to dispose
suspend fun <T> Single<T>.await(): T = suspendCancellableCoroutine { cont ->
subscribe(object : SingleObserver<T> {
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() }
}
})
}
42. And this is it
suspend fun <T> Single<T>.await(): T = suspendCancellableCoroutine { cont ->
subscribe(object : SingleObserver<T> {
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() }
}
})
}
45. 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)
}
46. Not idiomatic way
fun loadImageAsync(url: String): Deferred<Image> = async { TODO() }
suspend fun processImage(url: String) {
val image = loadImageAsync(url).await()
imageCache.add(image)
}
Don’t define async functions
in the first place
47. 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
49. 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
50. 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
51. 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
52. 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
53. 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