Day3:第一个 Coroutine 程序

在上一篇文章中,我们知道如果我们要以非同步的方式来执行,可以使用 Thread + callback 来写,不过使用 Callback 可能会发生两个问题,一是 callback hell;另一则是控制权转移 (Inversion of Control),让程序码变得不容易维护。

本篇文章,就让我们尝试使用 Coroutine 来改写吧。

加上 Dependency

要在专案里面使用 Coroutine ,必须先加上 dependency,如下:

dependencies{
	implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2-native-mt")
}

如果是 Android 的话,需要使用下方的 dependency,因为 Coroutine 与 Android 的 Jetpack 有整合,所以使用下方的 dependency

  • Android
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2")
}

动手改写

前面我们定义了三个函式如下:

fun login(userName: String, password: String): Token { ... }
fun fetchLatestContent(token: Token): List<Contents> { ... }
fun showContents(contents: List<Contents>){ ... }

fun showContents() {
    val token = login(userName, password)
    val contents = fetchLatestContents(token)
    showContents(contents)
}

改写步骤:

  1. 先将函式改为 suspend function (暂停函式)
suspend fun login(userName: String, password: String): Token { ... }

suspend fun fetchLatestContentAsync(token: Token): List<Contents> { ... }
  1. 将原本的函式 showContents() 改成 Coroutine 的写法
suspend fun showContents() = coroutineScope {
    launch {
        val token = login(userName, password)
        val content = fetch(token)
        withContext(Dispatchers.Main) {
            showContents(content)
        }
    }
}

到这边我们就用 Coroutine 完成了一个非同步的程序,是不是很容易呢?看起来是不是很像是同步的程序码呢?

回头看一下我们做了哪些改动:

  1. 在非同步的函式中加上了 suspend
  2. showContents() 使用 coroutineScope 将程序码包起来。
  3. login() 以及 fetch()launch{} 中执行。
  4. 最後,使用 withContext(Dispatchers.Main)showContents(content) 包起来。

来看一下这些改动分别代表什麽意思吧>>>

1. 在非同步的函式中加上了 suspend

Kotlin 的 Coroutine 用 suspend 这个关键字来宣告该函式为可暂停的函式。

2. showContents() 使用 coroutineScope 将程序码包起来。

前面我们定义了 suspend function ,这些可暂停的函式只能在 Coroutine 中使用,而 coroutineScope 是用来建立一个新的范围 (Scope),在 {} 内部的程序码,就是称为在 Coroutine 里面的程序。

3. login() 以及 fetch()launch{} 中执行。

launch{} 的功能类似 coroutineScope ,它也是用来定义一个 Coroutine 的范围,不过与 coroutineScope 不同的是,它会新建 coroutine ,而且它有一个回传值 Job

4. 使用 withContext(Dispatchers.Main)showContents(content) 包起来

我们可以把 coroutine 想成是执行绪(thread)的概念,利用背景的执行绪来执行耗时动作,完成之後,在由主执行绪来绘制画面。

前方的 launch{} 可以看作是建立一个新的执行绪,到了 withContext(Dispatchers.Main) 把 context 切回 Dispatchers.Main 也就是主执行绪,在主执行绪上进行画面的更新。


使用 Coroutine 的好处

到这边我们已经完成了一个简单的 Coroutine 程序。我们注意到,使用 Coroutine,我们就不需要在非同步函式完成之後使用 Callback 将结果传出来。不知道你有没有发现,用 Coroutine 完成的程序码可以让非同步程序码以同步的程序码来撰写。

另外,我们可以轻松的切换执行绪,利用不同的 Coroutine scope 来做不同的事,如上方的程序码,我们使用 launchlogin()fetch() 让它们在背景运算,并且在执行完毕之後,我们使用 withContext(Dispatchers.Main) 将执行绪切回主执行绪,并在主执行绪上更新画面。

心智图

心智图

特别感谢

Kotlin Taiwan User Group
Kotlin 读书会


<<:  视觉设计(1)

>>:  < 关於 React: 开始打地基| useState()>

Day23 用python写UI-聊聊Menu

第一次压线发文耶,今天真的有点忙,到现在才发文。Menu就是功能表的表单,通常都会在介面的最上面,是...

Day22 X Web Workers

身为前端开发者,整日与 JavaScript 这门程序语言打交道,应该都知道它是一个 single...

详解资料仓库的实施步骤,实战全解!(1)

建立资料仓库是一个解决企业资料问题应用的过程,是企业资讯化发展到一定阶段必不可少的一步,也是发展资料...

JavaScript学习日记 : Day25 - Set

Set与Map不同再於Set没有key,是指有包含值的特殊集合,且每个值只能出现一次不能重复。 Se...

从 JavaScript 角度学 Python(25) - 例外处理

前言 接下来我们要聊一个非常非常重要的东西,也就是关於错误的处理,而这个处理又称之为例外处理。 例外...