Callback hell(🤯) killer —— Coroutines😊

750 阅读3分钟

如果你觉得写callbacks成本也不大?嗯,那可能被callbacks毒打的还不够,需要丢个王炸出来💣.

无可厚非,每一次新的变革到来之前,我们都会习惯性将旧的规则当作信仰,直到先驱者为他们打开新大陆.

想一想:当Callbacks方法变多 + 嵌套层数变多的时候,耳熟能详的“回调地狱”就出来了👇:

实际需求

想象一下,如果我们有这么一个需求:查找某个用户的好友列表中,第N个好友的最新动态.

翻译成程序步骤:

首先,步骤如右👉:查询该用户信息 —> 查找该用户的好友列表 —> 查找第N个好友的最新动态.

其中,每一步出问题了,我们都需要把对应的信息在UI线程中展现出来(切换线程).

Callbacks方案

  • 观感:回调地狱,迷失在代码中.
  • 问题:回调代码的处理分散;线程任务管理复杂,可能产生泄漏. 有回调的世界里,我们可能一直要问自己:

    • 这个函数会不会产生一个后台任务?
    • 这个函数虽然返回了,但是它所产生的后台任务可能还在运行,它什么时候会结束?
    • 作为调用者,我应该在哪里、以怎样的方式处理回调?
    • 我需要保持这个函数用到的资源吗?后台任务会自动去持有这些资源吗?我需要自己去释放它们吗?
    • 派发出去的任务会不会再去派发别的任务?别的这些任务会被正确管理吗?如果取消了这个派发出去的任务,那些被二次派发的任务也会被正确取消吗?
//主线程中执行
fun onCreate() {
    //step1: 查询该用户的信息
    getUserInfo(uid, object: CallBack() {
        override fun onSuccess(user: User) {
            //step2:获取该用户的好友列表
            getFriendList(user, object: CallBack() {
                    override fun onSuccess(friendList: List<User>) {
                        //step3:获取第n个用户的最新动态
                        getFeedList(friendList[n], object: CallBack() {
                                override fun onSuccess(feed: List<String>) {
                                    //展示Feed
                                    runOnUiThread {
                                        showFeedList(feed)
                                    }
                                }
                                
                                override fun onFail(error: String) {
                                    //展示错误toast
                                    runOnUiThread {
                                        showErrorToast(error)
                                    }
                                }
                            });
                    }
                
                    override fun onFail(error: String) {
                        //展示错误toast
                        runOnUiThread {
                            showErrorToast(error)
                        }
                    }
                });
        }
    
        override fun onFail(error: String) {
            //展示错误toast
            runOnUiThread {
                showErrorToast(error)
            }
        }
    });
}

fun onDestory() {
    //question: 如何取消三个嵌套callback里的异步线程任务?嵌套的线程任务多了,哪种方式成本都不小. 实现不好,还容易出问题.
    
}

Coroutines方案

  • 观感:顺流而下,一目了然.
  • 便利:没有回调,可以统一管理Coroutines,不易产生泄漏.
//主线程中执行
val job: Job? = null

fun onCreate() {
    job = coroutineScope(Dispatchers.Main) {
        try {
            //step1: 查询该用户的信息
            val user = getUserInfo(uid)
            //step2:获取该用户的好友列表
            val friendList = getFriendList(user)
            //step3:获取第一个用户的最新动态
            val feedList = getFeedList(friendList[0].uid)
            //展示Feed
            withContext(Dispatchers.Main) {
                showFeedList(feed)
            }
        } catch(e: Exception) {
            //展示错误toast
            withContext(Dispatchers.Main) {
                showErrorToast(e.message)
            }
        }
    }
}

fun onDestroy() {
    //一键取消所有的下属Coroutines任务,且不依赖线程的取消.
    job?.cancel()
}
  • How?

    • 使用上多添加一行suspend关键字即可.
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
    }
}