$30 off During Our Annual Pro Sale. View Details »

Kotlin 1.3 Webinar Coroutines Presentation

Kotlin 1.3 Webinar Coroutines Presentation

Kotlin Coroutines is a foundational feature in Kotlin language that enables easy-to-write/easy-to-read asynchronous programming and more. For those of you who are familiar with other asynchronous programming abstractions like callbacks, futures/promises, and async/await, we’ll give an overview of what are the similarities and differences, and highlight the key features of Kotlin’s design.

Roman Elizarov

December 05, 2018
Tweet

More Decks by Roman Elizarov

Other Decks in Programming

Transcript

  1. Kotlin Coroutines
    Asynchronous programming with ease

    View Slide

  2. How do we write code that waits for
    something most of the time?

    View Slide

  3. Asynchronous Programming with
    Callbacks

    View Slide

  4. Callbacks: before
    fun requestToken(): Token {
    // makes request for a token & waits
    return token // returns result when received
    }
    1

    View Slide

  5. Callbacks: after
    fun requestTokenAsync(cb: (Token) -> Unit) {
    // makes request for a token, invokes callback when done
    // returns immediately
    }
    1
    callback

    View Slide

  6. 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

    View Slide

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

    View Slide

  8. 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)
    }

    View Slide

  9. 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

    View Slide

  10. Futures/Promises
    make it nicer

    View Slide

  11. Futures: before
    fun requestTokenAsync(cb: (Token) -> Unit) {
    // makes request for a token, invokes callback when done
    // returns immediately
    }
    1

    View Slide

  12. Futures: after
    fun requestTokenAsync(): Promise {
    // makes request for a token
    // returns promise for a future result immediately
    }
    1
    future

    View Slide

  13. Futures: before
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item,
    cb: (Post) -> Unit) {
    // sends item to the server, invokes callback when done
    // returns immediately
    }
    2

    View Slide

  14. Futures: after
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise {
    // sends item to the server
    // returns promise for a future result immediately
    }
    future
    2

    View Slide

  15. Futures: before
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise …
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync { token ->
    createPostAsync(token, item) { post ->
    processPost(post)
    }
    }
    }

    View Slide

  16. Futures: after
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise …
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync()
    .thenCompose { token -> createPostAsync(token, item) }
    .thenAccept { post -> processPost(post) }
    }

    View Slide

  17. Futures: after
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise …
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync()
    .thenCompose { token -> createPostAsync(token, item) }
    .thenAccept { post -> processPost(post) }
    }
    Callbacks are still here

    View Slide

  18. Futures: after
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise …
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync()
    .thenCompose { token -> createPostAsync(token, item) }
    .thenAccept { post -> processPost(post) }
    }
    Composable &
    propagates exceptions
    No nesting indentation Callbacks are still here

    View Slide

  19. Futures: after
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise …
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync()
    .thenCompose { token -> createPostAsync(token, item) }
    .thenAccept { post -> processPost(post) }
    }
    But all those operators…

    View Slide

  20. Kotlin coroutines
    to the rescue

    View Slide

  21. Coroutines: before
    fun requestTokenAsync(): Promise {
    // makes request for a token
    // returns promise for a future result immediately
    }
    1

    View Slide

  22. Coroutines: after
    suspend fun requestToken(): Token {
    // makes request for a token & suspends
    return token // returns result when received
    }
    1
    natural signature

    View Slide

  23. Coroutines: before
    suspend fun requestToken(): Token { … }
    fun createPostAsync(token: Token, item: Item): Promise {
    // sends item to the server
    // returns promise for a future result immediately
    }
    2

    View Slide

  24. 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
    natural signature

    View Slide

  25. 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) }
    }

    View Slide

  26. 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)
    }

    View Slide

  27. 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

    View Slide

  28. 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

    View Slide

  29. • Regular loops
    Bonus features
    for ((token, item) in list) {
    createPost(token, item)
    }

    View Slide

  30. • Regular exception handing
    Bonus features
    try {
    createPost(token, item)
    } catch (e: BadTokenException) {

    }

    View Slide

  31. • Regular higher-order functions
    Bonus features
    file.readLines().forEach { line ->
    createPost(token, line.toItem())
    }

    View Slide

  32. • Custom higher-order functions
    Bonus features
    val post = retryIO {
    createPost(token, item)
    }
    Everything like in regular code

    View Slide

  33. How does it work?
    A quick peek behind the scenes

    View Slide

  34. Kotlin suspending functions
    callback
    Kotlin
    Java/JVM
    suspend fun createPost(token: Token, item: Item): Post { … }
    Object createPost(Token token, Item item, Continuation cont) { … }

    View Slide

  35. Kotlin suspending functions
    callback
    Kotlin
    Java/JVM
    Continuation is a generic callback interface
    suspend fun createPost(token: Token, item: Item): Post { … }
    Object createPost(Token token, Item item, Continuation cont) { … }
    interface Continuation {
    val context: CoroutineContext
    fun resumeWith(result: Result)
    }

    View Slide

  36. Kotlin suspending functions
    callback
    Kotlin
    Java/JVM
    suspend fun createPost(token: Token, item: Item): Post { … }
    Object createPost(Token token, Item item, Continuation cont) { … }
    interface Continuation {
    val context: CoroutineContext
    fun resumeWith(result: Result)
    }

    View Slide

  37. Kotlin suspending functions
    callback
    Kotlin
    Java/JVM
    suspend fun createPost(token: Token, item: Item): Post { … }
    Object createPost(Token token, Item item, Continuation cont) { … }
    interface Continuation {
    val context: CoroutineContext
    fun resumeWith(result: Result)
    }

    View Slide

  38. Integration
    Zoo of futures on JVM

    View Slide

  39. interface Service {
    fun createPost(token: Token, item: Item): Call
    }
    Retrofit async

    View Slide

  40. interface Service {
    fun createPost(token: Token, item: Item): Call
    }
    suspend fun createPost(token: Token, item: Item): Post =
    serviceInstance.createPost(token, item).await()
    natural signature

    View Slide

  41. interface Service {
    fun createPost(token: Token, item: Item): Call
    }
    suspend fun createPost(token: Token, item: Item): Post =
    serviceInstance.createPost(token, item).await()
    Suspending extension function
    from integration library

    View Slide

  42. suspend fun Call.await(): T {

    }

    View Slide

  43. Callbacks everywhere
    suspend fun Call.await(): T {
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    // todo
    }
    override fun onFailure(call: Call, t: Throwable) {
    // todo
    }
    })
    }

    View Slide

  44. suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }

    View Slide

  45. suspend fun suspendCoroutine(block: (Continuation) -> Unit): T

    View Slide

  46. suspend fun suspendCoroutine(block: (Continuation) -> Unit): T

    View Slide

  47. suspend fun suspendCoroutine(block: (Continuation) -> Unit): T
    Regular function
    Inspired by call/cc from Scheme

    View Slide

  48. suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }

    View Slide

  49. Install callback
    suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }

    View Slide

  50. Install callback
    suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }

    View Slide

  51. Analyze response
    suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }

    View Slide

  52. Analyze response
    suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }
    That’s all

    View Slide

  53. Coroutine builders

    View Slide

  54. 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)
    }

    View Slide

  55. 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)
    }

    View Slide

  56. 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

    View Slide

  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)
    }
    Can suspend execution

    View Slide

  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)
    }
    Can suspend execution
    A regular function cannot

    View Slide

  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
    A regular function cannot
    One cannot simply invoke a
    suspending function

    View Slide

  60. Launch
    fun postItem(item: Item) {
    GlobalScope.launch {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    coroutine builder

    View Slide

  61. fun postItem(item: Item) {
    GlobalScope.launch {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    Fire and forget!

    View Slide

  62. fun postItem(item: Item) {
    GlobalScope.launch {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    Fire and forget!
    We launch for its
    side effects

    View Slide

  63. fun postItem(item: Item) {
    GlobalScope.launch {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    Returns immediately, coroutine works
    in background thread pool

    View Slide

  64. fun postItem(item: Item) {
    GlobalScope.launch {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }

    View Slide

  65. fun postItem(item: Item) {
    GlobalScope.launch(Dispatchers.Main) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    Dispatcher
    Just specify the dispatcher

    View Slide

  66. fun postItem(item: Item) {
    GlobalScope.launch(Dispatchers.Main) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    Dispatcher
    And it gets executed on the Main thread

    View Slide

  67. Where’s the magic of launch?

    View Slide

  68. fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend () -> Unit
    ): Job { … }
    A regular function

    View Slide

  69. fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend () -> Unit
    ): Job { … } suspending lambda

    View Slide

  70. fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend () -> Unit
    ): Job { … }

    View Slide

  71. async / await
    The classic approach

    View Slide

  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) { … }

    View Slide

  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 requestToken() { … }
    async Task createPost(Token token, Item item) { … }
    void processPost(Post post) { … }

    View Slide

  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 requestToken() { … }
    async Task createPost(Token token, Item item) { … }
    void processPost(Post post) { … }

    View Slide

  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 requestToken() { … }
    async Task createPost(Token token, Item item) { … }
    void processPost(Post post) { … }

    View Slide

  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 requestToken() { … }
    async Task createPost(Token token, Item item) { … }
    void processPost(Post post) { … }

    View Slide

  77. Why no await keyword in Kotlin?
    The problem with async
    requestToken() VALID –> produces Task
    await requestToken() VALID –> produces Token
    concurrent behavior
    sequential behavior
    C#
    C#
    default

    View Slide

  78. Kotlin suspending functions are
    designed to imitate sequential
    behavior by default
    Concurrency is hard
    Concurrency has to be explicit

    View Slide

  79. Kotlin approach to async
    Concurrency where you need it

    View Slide

  80. Use-case for async
    async Task loadImageAsync(String name) { … }
    C#

    View Slide

  81. Use-case for async
    var promise1 = loadImageAsync(name1);
    var promise2 = loadImageAsync(name2);
    async Task loadImageAsync(String name) { … }
    Start multiple operations
    concurrently
    C#

    View Slide

  82. Use-case for async
    var promise1 = loadImageAsync(name1);
    var promise2 = loadImageAsync(name2);
    var image1 = await promise1;
    var image2 = await promise2;
    async Task loadImageAsync(String name) { … }
    and then wait for them
    C#

    View Slide

  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 loadImageAsync(String name) { … }

    View Slide

  84. Kotlin async function
    fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    Kotlin

    View Slide

  85. Kotlin async function
    fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    Kotlin
    A regular function

    View Slide

  86. Kotlin async function
    fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    Kotlin’s future type
    Kotlin

    View Slide

  87. Kotlin async function
    fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    async coroutine builder
    Kotlin

    View Slide

  88. fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    val deferred1 = loadImageAsync(name1)
    val deferred2 = loadImageAsync(name2)
    Start multiple operations
    concurrently
    Kotlin

    View Slide

  89. fun loadImageAsync(name: String): Deferred =
    GlobalScope.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

    View Slide

  90. fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    val deferred1 = loadImageAsync(name1)
    val deferred2 = loadImageAsync(name2)
    val image1 = deferred1.await()
    val image2 = deferred2.await()
    val result = combineImages(image1, image2)
    Kotlin
    But what if this crashes?
    Crash?

    View Slide

  91. fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    val deferred1 = loadImageAsync(name1)
    val deferred2 = loadImageAsync(name2)
    val image1 = deferred1.await()
    val image2 = deferred2.await()
    val result = combineImages(image1, image2)
    Kotlin
    Crash?
    This one leaks
    But what if this crashes?

    View Slide

  92. Idiomatic: Using async function when needed
    suspend fun loadImage(name: String): Image { … }
    Is defined as suspending function, not async

    View Slide

  93. Idiomatic: Using async function when needed
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }
    Structured concurrency

    View Slide

  94. Idiomatic: Using async function when needed
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }

    View Slide

  95. Idiomatic: Using async function when needed
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }

    View Slide

  96. Idiomatic: Using async function when needed
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }

    View Slide

  97. Kotlin approach to async
    requestToken() VALID –> produces Token
    async { requestToken() } VALID –> produces Deferred
    sequential behavior
    concurrent behavior
    Kotlin
    Kotlin
    default

    View Slide

  98. Structured concurrency

    View Slide

  99. Structured concurrency
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }

    View Slide

  100. Crash
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }
    Crashes?

    View Slide

  101. Cancels scope
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }
    Cancels

    View Slide

  102. Cancels children
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }
    Cancels

    View Slide

  103. Kotlin suspending functions
    may use concurrency internally,
    but don’t leak it
    Concurrency needs to be structured

    View Slide

  104. Parallelism is optional

    View Slide

  105. Beyond asynchronous code
    Kotlin’s approach to generate/yield – synchronous coroutines

    View Slide

  106. Fibonacci sequence
    val fibonacci: Sequence = …

    View Slide

  107. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }

    View Slide

  108. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    println(fibonacci.take(10).toList())

    View Slide

  109. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    println(fibonacci.take(10).toList())
    >> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

    View Slide

  110. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    A coroutine builder with
    restricted suspension

    View Slide

  111. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    A suspending function in
    the scope of buildSequence

    View Slide

  112. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    println(fibonacci.take(10).toList())
    Synchronous

    View Slide

  113. Library vs Language
    Keeping the core language small

    View Slide

  114. Classic async
    async/await
    generate/yield
    Keywords

    View Slide

  115. Kotlin coroutines
    suspend Modifier

    View Slide

  116. Kotlin coroutines
    Standard
    library

    View Slide

  117. Kotlin coroutines
    Standard
    library
    kotlinx-coroutines
    launch, async, runBlocking,
    Job, Deferred, etc
    http://github.com/kotlin/kotlinx.coroutines

    View Slide

  118. View Slide

  119. Thank you
    Any questions?
    elizarov @
    Roman Elizarov
    relizarov

    View Slide