If you think it doesn’t cost much to write callbacks, right? Well, that may be callbacks beat is not enough, need to throw a king fried out 💣.
Sure enough, we are used to believing in the old rules before each new revolution arrives, until the pioneers open up a new world for them.
Think about it: when there are more Callbacks + more nested layers, the well-known “callback hell” comes out 👇 :
Actual demand
Imagine if we had a requirement to find the latest activity of the NTH friend in a user’s friends list.
Translated into procedural steps:
First, the steps as right 👉 : query the user’s information – > find the user’s friends list – > find the latest news of the NTH friend.
For each step that goes wrong, we need to present the corresponding information in the UI thread (switch threads).
Callbacks scheme
- Look and feel: Callback hell, lost in code.
-
Problem: Scattered processing of callback code; Thread task management is complex and can cause leaks. In a world with pullbacks, we might always ask ourselves:
- Does this function produce a background task?
- This function returns, but the background tasks it creates may still be running. When does it end?
- As the caller, where and how should I handle the callback?
- Do I need to keep the resources used by this function? Do background tasks automatically hold these resources? Do I need to release them myself?
- Will the assigned tasks be assigned to other tasks? Will these other tasks be managed correctly? If the dispatched task is cancelled, will the second dispatched task also be cancelled correctly?
GetUserInfo (uid, object: CallBack() {override fun onSuccess(user: CallBack); GetFriendList (User, object: CallBack() {override fun onSuccess(friendList: GetFeedList (friendList[n], object: CallBack() {override fun onSuccess(feed: List<String>) {// Display Feed runOnUiThread {showFeedList(Feed)}} Override fun onFail(error: String) {// Display error toast runOnUiThread {showErrorToast(error)}}}); } Override fun onFail(error: String) {// Display error toast runOnUiThread {showErrorToast(error)}}}); } Override fun onFail(error: String) {// Display error toast runOnUiThread {showErrorToast(error)}}}); } fun onDestory() {//question: How to cancel three nested asynchronous thread tasks in callback? There are a lot of nested thread tasks, which is not a small cost. Poor implementation and problem prone.Copy the code
Coroutines scheme
- View: Downstream, at a glance.
- Convenience: No callbacks, unified management of Coroutines, no leaks.
Val job: job? = null fun onCreate() { job = coroutineScope(Dispatchers.Main) { try { //step1: Val user = getUserInfo(uid) //step2: Get the friendList of the user val friendList = getFriendList(user) //step3: Val feedList = getFeedList(friendList[0].uid) // Show Feed withContext(dispatchers.main) {showFeedList(Feed)} } catch(e: Exception) {// Show the error toast withContext(dispatchers.main) {showErrorToast(e.message)}}}} fun onDestroy() { // Cancel all subordinate Coroutines tasks with one click, independent of thread cancellation. .cancel() }Copy the code
-
How?
- Use one more line of the suspend keyword.
suspend fun getUserInfo(uid: String): User {
return withContext(Dispatcher.IO) {
//network request code
}
}
suspend fun getFriendList(user: User): List<User> {
return withContext(Dispatcher.IO) {
//network request code
}
}
suspend fun getFeedList(uid: String): List<Feed> {
return withContext(Dispatcher.IO) {
//network request code
}
}
Copy the code