preface

Hello everyone, I’m Xiao Yi! In the previous chapter, we had a brief understanding of what coroutines are and the basic use of coroutines, mainly mentioning the use of launch and withContext. But launch and withContext are not suitable for concurrent scenarios where results need to be returned, where we typically use coroutines async/await.

recommended

This article will be first published on the public account “Code Man” and personal blog “Li Yi’s small station”. If you think this article is helpful to you, please pay attention to the public account! This article will be first published on the public account “Code Man” and personal blog “Li Yi’s small station”. If this article is helpful to you, please pay attention to the public account!

I. Async and AWIAT

Async refers to asynchrony in its literal sense. Async is also an extension function of CoroutineScope, its source code is as follows:

public fun <T> CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T> {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyDeferredCoroutine(newContext, block) else
        DeferredCoroutine<T>(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}
Copy the code

Async, like Launch, creates a new coroutine, but Launch returns Job while Async returns Deferred (a subclass of Job). Here is a quick example of how async can be used:

coroutineScope.launch(Dispatchers.IO) {
	val a = async{ getUserInfo() }
	// Execute the coroutine
	val userInfo = a.await()
}
Copy the code

Val a = async{getUserInfo()} does not actually execute getUserInfo(), but returns a Deferred object named A. A.await () is the real start to execute the coroutine so that getUserInfo() gets executed and returns userInfo (**await() the data type returned depends on the type returned by async{}** last line). So it can be concluded that async is just creating and await is executing. In addition, async can also specify a running thread:

 val a = async(Dispatchers.IO){ getUserInfo() }
Copy the code

Second, the concurrent

After learning the basic usage of async/await, let’s talk about using async/await to do concurrency, again with a small example!

1. Case 1

coroutineScope.launch(Dispatchers.IO) {
	val a1 = async{ getUserInfo() }
	val userInfo = a1.await()
	val a2 = async{ getMessage(userInfo.token) }
	val msgList = a2.await()
}
Copy the code

In case 1, getUserInfo() is executed first, and getMessage() is executed after getUserInfo(), in order.

2. Case 2

coroutineScope.launch(Dispatchers.IO) {
	val a1 = async{ getUserInfo() }
	val a2 = async{ getHomeInfo() }
	val userInfo = a1.await()
	val homeInfo = a2.await()
}
Copy the code

In case 2, getUserInfo() and getHomeInfo() are executed concurrently. Here may have a homecoming doubt, why not a1. Await execution again after a2 () has been completed. Await (), such cases and not simply a contradiction? Await () function has a property that, when await() is executed, it will not only execute its corresponding async, it will cause all previous async to execute. Remember this property.

In case 2, a1.await() is a fuse, the execution of a1.await() causes async{getUserInfo()} and async{getHomeInfo()}, which were not executed previously, to start executing. A2.await () is just to getHomeInfo and does not perform async{getHomeInfo()}. To be more rude, getHomeInfo() in case 2 will be executed even without val homeInfo = a2.await(). In case 1, async -> await -> async -> await is used, so only one async will be executed at a time, in order.

Third, suspend

Suspend literally means to suspend and pause, and is used primarily in coroutines to modify functions. It also does what it literally does, suspending the function. The flow can be understood as follows: the coroutine encounters a function marked suspend while executing, and the function is switched to another thread. The execution of the code is in the function that is switched out, so the coroutine code behind the function is not executed. When this function is finished, the coroutine is switched back to the original thread, code execution is reverted to the original thread, and the coroutine code after this function is continued.

To get a clear view of suspend, let’s use a small example:

fun init(a) {
	coroutineScope.launch {
		val userInfo = getUserInfo()
		tv_name.text = userInfo.name
	}
}

// Request user information
suspend fun getUserInfo(a): UserInfo {
	returnwithContext(Dispatchers.IO){ ... }}Copy the code

In the above example, init has a coroutine that requests user information using getUserInfo(), using the suspend modifier. When the getUserInfo() method is executed, the execution of getUserInfo() is switched from the main thread to the IO thread. The current code execution is suspended on the getUserInfo() method, tv_name.text = userinfo.name, until the getUserInfo() method completes, and the code execution is suspended from the IO thread back to the main thread. Tv_name. Text = userinfo. name Continue.

In fact, if you look at the source code, you’ll see that many methods like withContext are suspend, which is why you can use them to code synchronously.

public suspend fun <T> withContext(
    context: CoroutineContext,
    block: suspend CoroutineScope.() -> T
): T = suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
	...
}
Copy the code

In addition, two points in particular are proposed here:

  • suspendIt’s just a flag to remind the coroutine to do something about it. The actual thread switching is done by the coroutine itself, not by the coroutinesuspendThe ability to modify the function itself
  • suspendModified functions can be held by otherssuspendFunction calls, but ultimately thesesuspendCall execution can only be done in coroutines and cannot be used in isolation like normal methods

Four, summary

So far, we have used two chapters to cover the basic use of coroutines. The next chapter will cover encapsulation of coroutines in MVVM. If you still have questions about the basics of coroutines, check out the documentation for coroutines.

Five, good articles recommended

  • “Take a hard look at Kotlin’s Coroutines – Can’t Learn coroutines? Probably because all the tutorials you read are wrong.”
  • “Kotlin coroutine hanging so weird and confusing? I skinned it today.”
  • What is a “non-blocking” hang, and Are Coroutines Really Lighter?