Coroutines Cheat Sheet

· 544 words · 3 minute read

Job 🔗

A failure of any child immediately causes the Job to fail and cancels the rest of its children.

val scope = CoroutineScope(Dispatchers.Main + Job())

scope.launch { // the first child 
    throw Exception()
}
scope.launch { // the second child 
    // canceled by the failure of the first child
}

SupervisorJob 🔗

A failure of a child does not cause the SupervisorJob to fail and does not affect its other children.

val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())

scope.launch { // the first child 
    throw Exception()
}
scope.launch { // the second child
    // NOT canceled by the failure of the first child
}

coroutineScope 🔗

A failure of a scope cancels all its children.

coroutineScope {
    val child = async {
        // canceled by the failure of the scope
    }
    throw Exception()
}

A failure of any child causes the scope to fail and the rest of the children are canceled.

coroutineScope {
    val child1 = async {
        throw Exception()
    }
    val child2 = async {
        // canceled by the failure of the first child
    }
    child1.await()
}

Since the failure of a child causes the scope itself to fail, using try-catch on a child will NOT work.

coroutineScope {
    try {
        val child = async { throw Exception() }.await()
    } catch (e: Exception) {
        // coroutineScope rethrows the exception
    }
}

Exception thrown by a child is rethrown by the coroutineScope and can be handled from the outer scope.

suspend fun someFunction() = coroutineScope {
    val child = async { throw  Exception() }.await()
}

val scope = CoroutineScope(Dispatchers.Main + Job())

scope.launch {
    try {
        someFunction()
    } catch (e: Exception) {
        // exception successfully caught
    }
}

supervisorScope 🔗

A failure of a scope cancels all its children.

supervisorScope {
    val child = async {
        // canceled by the failure of the scope
    }
    throw Exception()
}

A failure of a child does not cause the scope to fail and does not affect its other children.

supervisorScope {
    val child1 = async {
        throw Exception()
    }
    val child2 = async {
        // NOT canceled by the failure of the first child
    }
    try {
        child1.await()
    } catch (e: Exception) {
        // exception successfully caught
    }
}

Exception thrown by the supervisorScope can be handled from the outer scope.

suspend fun someFunction() = supervisorScope {
    val child = async { throw  Exception() }.await()
}

val scope = CoroutineScope(Dispatchers.Main + Job())

scope.launch {
    try {
        someFunction()
    } catch (e: Exception) {
        // exception successfully caught
    }
}

suspendCoroutine 🔗

Suspends currently running coroutine. Provided continuation has two functions: resume and resumeWithException, calling either function will cause suspendCoroutine to resume immediately. Typically used to convert callback-based APIs to suspend functions.

suspend fun <T> Call<T>.await(): T {
    return suspendCoroutine { continuation ->
        this.enqueue(object : Callback<T> {
            override fun onResponse(call: Call<T>, response: Response<T>) {
                continuation.resume(response.body()!!)
            }

            override fun onFailure(call: Call<T>, t: Throwable) {
                continuation.resumeWithException(t)
            }
        })
    }
}

suspendCancellableCoroutine 🔗

Works similarly to suspendCoroutine but supports cancellation.

suspend fun <T> Call<T>.await(): T {
    return suspendCancellableCoroutine { continuation ->
        this.enqueue(object : Callback<T> {
            override fun onResponse(call: Call<T>, response: Response<T>) {
                continuation.resume(response.body()!!)
            }

            override fun onFailure(call: Call<T>, t: Throwable) {
                continuation.resumeWithException(t)
            }
        })

        continuation.invokeOnCancellation {
            this.cancel()
        }
    }
}

Channel 🔗

Hot stream: The data is being produced regardless of the presence of subscriber.

Flow 🔗

Cold stream