Upgrade to Pro — share decks privately, control downloads, hide ads and more …

9. Introduction to Coroutines [kotlin-workshop]

9. Introduction to Coroutines [kotlin-workshop]

Part of https://github.com/jetBrains/kotlin-workshop.

Covers:
- Comparing threads and coroutines
- async/await
- Programming with suspend functions

Similar talk was presented here: https://vimeo.com/239334237.

Svetlana Isakova

November 01, 2017
Tweet

More Decks by Svetlana Isakova

Other Decks in Programming

Transcript

  1. Kotlin Coroutines • the key new feature in Kotlin 1.1

    • simplify asynchronous programming
  2. Coroutine a main routine and a subroutine vs coroutines, which

    call on each other • the term from 1960s • was used in “The Art of Computer Programming” by Donald Knuth
  3. C# way async Task ProcessImage(String url) { var image =

    await LoadImage(url); SetImage(image); } Kotlin way fun processImage() = async { val image = loadImageAsync().await() setImage(image) }
  4. Coroutine is similar to a thread Thread: • a sequence

    of instructions Multiple threads: • can be executed concurrently • share resources such as memory Coroutine: Multiple coroutines:
  5. Executor • fixed number of threads • adding tasks •

    but: difficult to manage dependencies
  6. 1 2 3 • computation that can be suspended •

    thread is not blocked! Coroutines
  7. 1 2 3 • computation that can be suspended •

    thread is not blocked! UI UI UI Coroutines
  8. fun loadImageAsync() = async { /* do the work */

    } fun processImage() = async { val image = loadImageAsync().await() setImage(image) } Back to image example
  9. suspending call fun processImage() = async { val image =

    loadImageAsync().await() setImage(image) } await suspends computation
  10. fun loadImageAsync(): Deferred<Image> = async { /* do the work

    */ } interface Deferred<out T> {
 suspend fun await(): T
 } await is a suspend function
  11. await suspends computation fun processImage() = async { val image

    = loadImageAsync().await() setImage(image) }
  12. fun processImage() = async { val deferred = loadImageAsync() val

    image = deferred.await() setImage(image) } await suspends computation
  13. await suspends computation processImage loadImageAsync 1 fun processImage() = async

    { val deferred = loadImageAsync() val image = deferred.await() setImage(image) }
  14. await suspends computation processImage loadImageAsync 1 loadImageAsync processImage 2 await

    fun processImage() = async { val deferred = loadImageAsync() val image = deferred.await() setImage(image) }
  15. await suspends computation loadImageAsync processImage 2 loadImageAsync processImage 3 …and

    continues it when result is ready fun processImage() = async { val deferred = loadImageAsync() val image = deferred.await() setImage(image) }
  16. Example: simple consecutive logic fun login(credentials: Credentials): UserID fun loadUserData(userID:

    UserID): UserData fun showData(data: UserData) fun showUserInfo(credentials: Credentials) { val userID = login(credentials) val userData = loadUserData(userID) showData(userData) }
  17. Rewrite with CompletableFuture fun loginAsync(credentials: Credentials): CompletableFuture<UserID> fun loadUserDataAsync(userID: UserID):

    CompletableFuture<UserData> fun showData(data: UserData) fun showUserInfo(credentials: Credentials) { loginAsync(credentials) .thenCompose { loadUserDataAsync(it) } .thenAccept { showData(it) } }
  18. Rewrite with async/await fun login(credentials: Credentials): Deferred<UserID> fun loadUserData(userID: UserID):

    Deferred<UserData> fun showData(data: UserData) fun showUserInfo(credentials: Credentials) = async { val userID = login(credentials).await() val userData = loadUserData(userID).await() showData(userData) }
  19. Rewrite with suspend functions suspend fun login(credentials: Credentials): UserID suspend

    fun loadUserData(userID: UserID): UserData fun showData(data: UserData) suspend fun showUserInfo(credentials: Credentials) { val userID = login(credentials) val userData = loadUserData(userID) showData(userData) }
  20. Q: Where can I call suspend functions? A: Inside other

    suspend functions and inside async, launch, etc. (so-called, coroutine builders).
  21. val deferred = async { … } deferred.await() val job

    = launch { … } job.join() runBlocking { … } Coroutine Builders start a coroutine
  22. fun <T> async( context: CoroutineContext, block: suspend CoroutineScope.() -> T

    ) : Deferred<T> fun launch( context: CoroutineContext, block: suspend CoroutineScope.() -> Unit ): Job suspend lambdas
  23. fun showUserInfo(credentials: Credentials) = launch(UI) {
 val userID = login(credentials)


    val data = loadUserData(userID)
 val image = async {
 loadImage(data.imageID)
 }
 showData(data)
 showImage(image.await())
 } Nested coroutines
  24. suspend fun foo(): Int suspend fun foo(continuation: Continuation<Int>): Int Hidden

    parameter: continuation Continuation is a generic callback interface: public interface Continuation<in T> { public val context: CoroutineContext public fun resume(value: T) public fun resumeWithException(exception: Throwable) }
  25. Q: Can I call a suspend function from Java? suspend

    fun foo(continuation: Continuation<Int>): Int A: You don’t want to call it directly (because of Continuation parameter).
  26. suspend fun foo(): Int fun fooAsync(): CompletableFuture<Int> = future {

    foo() } fun fooAsync(): Single<Int> = rxSingle(CommonPool) { foo() } To call suspend foo from Java wrap it into fooAsync if you have Java 8 if you have RxJava
  27. fun loadUserData(userId: UserID, callback: (UserData?, Throwable?) -> Unit) suspend fun

    loadUserData(userId: UserID): UserData { return suspendCoroutine { continuation -> loadUserData(userId) { data, exception -> if (data != null) { continuation.resume(data) } else { continuation.resumeWithException(exception!!) } } } } Wrapping third-party callback-based API
  28. fun loadUserDataRx(userId: UserID): Single<UserData> suspend fun loadUserData(userId: UserID): UserData {

    return suspendCoroutine { continuation -> loadUserDataRx(userId) .doOnSuccess { continuation.resume(it) } .doOnError { continuation.resumeWithException(it) } .subscribe() } } Wrapping a function returning rx.Single or just loadUserDataRx(userId).await()
  29. Experimental status of Coroutines • We want the community to

    try it! • Migration aids will be provided • Old code will continue to work via the support library