Tackling Asynchrony
with Kotlin Coroutines
Presented By: PRAVEER GUPTA
fun getUserAddress(userId: Long): Address { … }
fun getRestaurants(address: Address): List<Restaurant> { … }
Sync or Async?
Reactive approach to Async
fun getUserAddress(userId: Long): Single<Address> { … }
fun getRestaurants(address: Address): Single<List<Restaurant>> { … }
Wrapped return types
Kotlin Coroutine’s approach to Async
suspend fun getUserAddress(userId: Long): Address { … }
suspend fun getRestaurants(address: Address): List<Restaurant> { … }
Suspend modifier
Sync API Usage
fun showUserRestaurants(userId: Long) {
val address = getUserAddress(userId)
val restaurants = getRestaurants(address)
processRestaurants(restaurants)
}
fun getUserAddress(userId: Long): Address { … }
fun getRestaurants(address: Address): List<Restaurant> { … }
Sequential & blocking
fun showUserRestaurants(userId: Long) {
getUserAddress(userId)
.flatMap { address -> getRestaurants(address) }
.subscribe { restaurants -> processRestaurants(restaurants) }
}
Reactive API Usage
fun getUserAddress(userId: Long): Single<Address> { … }
fun getRestaurants(address: Address): Single<List<Restaurant>> { … }
Chained operators
& actions
Kotlin Coroutine API Usage
suspend fun showUserRestaurants(userId: Long) {
val address = getUserAddress(userId)
val restaurants = getRestaurants(address)
processRestaurants(restaurants)
}
suspend fun getUserAddress(userId: Long): Address { … }
suspend fun getRestaurants(address: Address): List<Restaurant> { … }
Suspend modifier
Comparison
suspend fun showUserRestaurants(userId: Long) {
val address = getUserAddress(userId)
val restaurants = getRestaurants(address)
processRestaurants(restaurants)
}
suspend fun getUserAddress(userId: Long): Address { … }
suspend fun getRestaurants(address: Address): List<Restaurant> { … }
fun showUserRestaurants(userId: Long) {
val address = getUserAddress(userId)
val restaurants = getRestaurants(address)
processRestaurants(restaurants)
}
fun getUserAddress(userId: Long): Address { … }
fun getRestaurants(address: Address): List<Restaurant> { … }
Synchronous
Asynchronous
using
Kotlin
Coroutines
Structure of
imperative synchronous code
is the same as
asynchronous code
(taken from KotlinConf 2018 - Exploring Coroutines in Kotlin by Venkat Subramaniam available on
YouTube)
Kotlin Coroutines
Kotlin Coroutines
No restructuring of code to a different paradigm
Reuse existing language control flows
Sequential vs Concurrent execution with coroutines
Complexities of async programming that coroutines handles
Kotlin Coroutines 101
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello")
}
Output:
Hello
World!
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello")
}
Coroutine Builders
Coroutine - an instance
of a suspendable
computation
Kotlin Coroutines 101
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello")
}
Suspending
function suspends
the execution of
the coroutine
Kotlin Coroutines 101
Suspended coroutine
resumes with a
continuation block
public interface Continuation<in T> {
public val context: CoroutineContext
public fun resumeWith(result: Result<T>)
}
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello")
}
Kotlin Coroutines 101
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
doWorld()
}
println("Hello")
}
private suspend fun doWorld() {
delay(1000L)
println("World!")
}
Suspending function is
marked with a
suspend modifier
Kotlin Coroutines 101
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2
org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.3.2
org.jetbrains.kotlinx:kotlinx-coroutines-reactive:1.3.2
Core Library
Integrations
Kotlin Coroutines 101
suspend fun showUserRestaurants(userId: Long) {
val address = getUserAddress(userId)
val restaurants = getRestaurants(address)
processRestaurants(restaurants)
}
Reuse existing Control flows
How can you make the below
method resilient to failures?
suspend fun showUserRestaurants(userId: Long) {
try {
val address = withTimeout(200L) { getUserAddress(userId) }
val restaurants = withTimeout(300L) { getRestaurants(address) }
processRestaurants(restaurants)
} catch (e: TimeoutCancellationException) {
// handle exception here
}
}
Exception handling using
standard try-catch block
Reuse existing Control flows
Sequential vs Concurrent
suspend fun showUserDetails(userId: Long) {
val userInfo = getUserInfo(userId)
val orders = getUserOrders(userId)
combineAndBuild(userInfo, orders)
}
Two independent
calls to
external system
suspend fun getUserInfo(userId: Long): UserInfo { … }
suspend fun getUserOrders(userId: Long): List<Order> { … }
suspend fun showUserDetails(userId: Long) {
val userInfo = getUserInfo(userId)
val orders = getUserOrders(userId)
combineAndBuild(userInfo, orders)
}
Sequential vs Concurrent
suspend fun getUserInfo(userId: Long): UserInfo { … }
suspend fun getUserOrders(userId: Long): List<Order> { … }
first suspension
Sequential vs Concurrent
suspend fun getUserInfo(userId: Long): UserInfo { … }
suspend fun getUserOrders(userId: Long): List<Order> { … }
suspend fun showUserDetails(userId: Long) {
val userInfo = getUserInfo(userId)
val orders = getUserOrders(userId)
combineAndBuild(userInfo, orders)
}
second suspension
Sequential vs Concurrent
suspend fun getUserInfo(userId: Long): UserInfo { … }
suspend fun getUserOrders(userId: Long): List<Order> { … }
suspend fun showUserDetails(userId: Long) {
val userInfo = getUserInfo(userId)
val orders = getUserOrders(userId)
combineAndBuild(userInfo, orders)
}
final call
Sequential vs Concurrent
suspend fun showUserDetails(userId: Long) {
val userInfo = getUserInfo(userId)
val orders = getUserOrders(userId)
combineAndBuild(userInfo, orders)
}
suspend fun getUserInfo(userId: Long): UserInfo { … }
suspend fun getUserOrders(userId: Long): List<Order> { … }
sequential
execution
by default
Sequential vs Concurrent
suspend fun showUserDetails(userId: Long) {
coroutineScope {
val userInfo = async { getUserInfo(userId) }
val orders = async { getUserOrders(userId) }
combineAndBuild(userInfo.await(), orders.await())
}
}
suspend fun getUserInfo(userId: Long): UserInfo { … }
suspend fun getUserOrders(userId: Long): List<Order> { … }
concurrently executed
suspension
happens here
Structured Concurrency
suspend fun showUserDetails(userId: Long) {
coroutineScope {
val userInfo = async { getUserInfo(userId) }
val orders = async { getUserOrders(userId) }
combineAndBuild(userInfo.await(), orders.await())
}
}
When can a resource leakage happen?
Structured Concurrency
suspend fun showUserDetails(userId: Long) {
coroutineScope {
val userInfo = async { getUserInfo(userId) }
val orders = async { getUserOrders(userId) }
combineAndBuild(userInfo.await(), orders.await())
}
}
Exception in a coroutine results in its cancellation
1
2
3
4
Kotlin Coroutines
No restructuring of code to a different paradigm
Reuse existing language control flows
Sequential vs Concurrent execution with coroutines
Complexities of async programming that coroutines handles
Call to Action
https://kotlinlang.org/docs/reference/coroutines-overview.html
https://www.youtube.com/results?search_query=kotlin+coroutines
Give it a try on your next project
Thank You
https://praveergupta.in/
https://praveer09.github.io/

Tackling Asynchrony with Kotlin Coroutines

  • 1.
    Tackling Asynchrony with KotlinCoroutines Presented By: PRAVEER GUPTA
  • 2.
    fun getUserAddress(userId: Long):Address { … } fun getRestaurants(address: Address): List<Restaurant> { … } Sync or Async?
  • 3.
    Reactive approach toAsync fun getUserAddress(userId: Long): Single<Address> { … } fun getRestaurants(address: Address): Single<List<Restaurant>> { … } Wrapped return types
  • 4.
    Kotlin Coroutine’s approachto Async suspend fun getUserAddress(userId: Long): Address { … } suspend fun getRestaurants(address: Address): List<Restaurant> { … } Suspend modifier
  • 5.
    Sync API Usage funshowUserRestaurants(userId: Long) { val address = getUserAddress(userId) val restaurants = getRestaurants(address) processRestaurants(restaurants) } fun getUserAddress(userId: Long): Address { … } fun getRestaurants(address: Address): List<Restaurant> { … } Sequential & blocking
  • 6.
    fun showUserRestaurants(userId: Long){ getUserAddress(userId) .flatMap { address -> getRestaurants(address) } .subscribe { restaurants -> processRestaurants(restaurants) } } Reactive API Usage fun getUserAddress(userId: Long): Single<Address> { … } fun getRestaurants(address: Address): Single<List<Restaurant>> { … } Chained operators & actions
  • 7.
    Kotlin Coroutine APIUsage suspend fun showUserRestaurants(userId: Long) { val address = getUserAddress(userId) val restaurants = getRestaurants(address) processRestaurants(restaurants) } suspend fun getUserAddress(userId: Long): Address { … } suspend fun getRestaurants(address: Address): List<Restaurant> { … } Suspend modifier
  • 8.
    Comparison suspend fun showUserRestaurants(userId:Long) { val address = getUserAddress(userId) val restaurants = getRestaurants(address) processRestaurants(restaurants) } suspend fun getUserAddress(userId: Long): Address { … } suspend fun getRestaurants(address: Address): List<Restaurant> { … } fun showUserRestaurants(userId: Long) { val address = getUserAddress(userId) val restaurants = getRestaurants(address) processRestaurants(restaurants) } fun getUserAddress(userId: Long): Address { … } fun getRestaurants(address: Address): List<Restaurant> { … } Synchronous Asynchronous using Kotlin Coroutines
  • 9.
    Structure of imperative synchronouscode is the same as asynchronous code (taken from KotlinConf 2018 - Exploring Coroutines in Kotlin by Venkat Subramaniam available on YouTube) Kotlin Coroutines
  • 10.
    Kotlin Coroutines No restructuringof code to a different paradigm Reuse existing language control flows Sequential vs Concurrent execution with coroutines Complexities of async programming that coroutines handles
  • 11.
    Kotlin Coroutines 101 importkotlinx.coroutines.* fun main() = runBlocking { launch { delay(1000L) println("World!") } println("Hello") } Output: Hello World!
  • 12.
    import kotlinx.coroutines.* fun main()= runBlocking { launch { delay(1000L) println("World!") } println("Hello") } Coroutine Builders Coroutine - an instance of a suspendable computation Kotlin Coroutines 101
  • 13.
    import kotlinx.coroutines.* fun main()= runBlocking { launch { delay(1000L) println("World!") } println("Hello") } Suspending function suspends the execution of the coroutine Kotlin Coroutines 101
  • 14.
    Suspended coroutine resumes witha continuation block public interface Continuation<in T> { public val context: CoroutineContext public fun resumeWith(result: Result<T>) } import kotlinx.coroutines.* fun main() = runBlocking { launch { delay(1000L) println("World!") } println("Hello") } Kotlin Coroutines 101
  • 15.
    import kotlinx.coroutines.* fun main()= runBlocking { launch { doWorld() } println("Hello") } private suspend fun doWorld() { delay(1000L) println("World!") } Suspending function is marked with a suspend modifier Kotlin Coroutines 101
  • 16.
  • 17.
    suspend fun showUserRestaurants(userId:Long) { val address = getUserAddress(userId) val restaurants = getRestaurants(address) processRestaurants(restaurants) } Reuse existing Control flows How can you make the below method resilient to failures?
  • 18.
    suspend fun showUserRestaurants(userId:Long) { try { val address = withTimeout(200L) { getUserAddress(userId) } val restaurants = withTimeout(300L) { getRestaurants(address) } processRestaurants(restaurants) } catch (e: TimeoutCancellationException) { // handle exception here } } Exception handling using standard try-catch block Reuse existing Control flows
  • 19.
    Sequential vs Concurrent suspendfun showUserDetails(userId: Long) { val userInfo = getUserInfo(userId) val orders = getUserOrders(userId) combineAndBuild(userInfo, orders) } Two independent calls to external system suspend fun getUserInfo(userId: Long): UserInfo { … } suspend fun getUserOrders(userId: Long): List<Order> { … }
  • 20.
    suspend fun showUserDetails(userId:Long) { val userInfo = getUserInfo(userId) val orders = getUserOrders(userId) combineAndBuild(userInfo, orders) } Sequential vs Concurrent suspend fun getUserInfo(userId: Long): UserInfo { … } suspend fun getUserOrders(userId: Long): List<Order> { … } first suspension
  • 21.
    Sequential vs Concurrent suspendfun getUserInfo(userId: Long): UserInfo { … } suspend fun getUserOrders(userId: Long): List<Order> { … } suspend fun showUserDetails(userId: Long) { val userInfo = getUserInfo(userId) val orders = getUserOrders(userId) combineAndBuild(userInfo, orders) } second suspension
  • 22.
    Sequential vs Concurrent suspendfun getUserInfo(userId: Long): UserInfo { … } suspend fun getUserOrders(userId: Long): List<Order> { … } suspend fun showUserDetails(userId: Long) { val userInfo = getUserInfo(userId) val orders = getUserOrders(userId) combineAndBuild(userInfo, orders) } final call
  • 23.
    Sequential vs Concurrent suspendfun showUserDetails(userId: Long) { val userInfo = getUserInfo(userId) val orders = getUserOrders(userId) combineAndBuild(userInfo, orders) } suspend fun getUserInfo(userId: Long): UserInfo { … } suspend fun getUserOrders(userId: Long): List<Order> { … } sequential execution by default
  • 24.
    Sequential vs Concurrent suspendfun showUserDetails(userId: Long) { coroutineScope { val userInfo = async { getUserInfo(userId) } val orders = async { getUserOrders(userId) } combineAndBuild(userInfo.await(), orders.await()) } } suspend fun getUserInfo(userId: Long): UserInfo { … } suspend fun getUserOrders(userId: Long): List<Order> { … } concurrently executed suspension happens here
  • 25.
    Structured Concurrency suspend funshowUserDetails(userId: Long) { coroutineScope { val userInfo = async { getUserInfo(userId) } val orders = async { getUserOrders(userId) } combineAndBuild(userInfo.await(), orders.await()) } } When can a resource leakage happen?
  • 26.
    Structured Concurrency suspend funshowUserDetails(userId: Long) { coroutineScope { val userInfo = async { getUserInfo(userId) } val orders = async { getUserOrders(userId) } combineAndBuild(userInfo.await(), orders.await()) } } Exception in a coroutine results in its cancellation 1 2 3 4
  • 27.
    Kotlin Coroutines No restructuringof code to a different paradigm Reuse existing language control flows Sequential vs Concurrent execution with coroutines Complexities of async programming that coroutines handles
  • 28.
  • 29.