协程进阶:选择 05. 实现 SwitchMap (Deferred 切换)

本篇解析 example-select-05.kt。探讨如何利用 select 处理动态切换的任务流。

1. 核心概念:任务切换 (Switching)

在响应式编程中,switchMap 是一个经典操作:当有新的任务到来时,立即抛弃旧任务的执行结果,转而监听新任务。

实现逻辑:

  • 多源监听:同时监听“新任务的进入”和“当前任务的完成”。
  • 动态更新:一旦新任务进入,立即通过 select 的循环机制将其设为 current,从而实现逻辑上的“覆盖”。

2. 代码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel<Deferred<String>>) = produce {
var current = input.receive() // 初始任务
while (isActive) {
val next = select<Deferred<String>?> {
input.onReceiveCatching { update -> // 监听新任务进入
update.getOrNull()
}
current.onAwait { value -> // 监听当前任务完成
send(value)
input.receiveCatching().getOrNull() // 任务完成后取下一个
}
}
if (next == null) break else current = next // 切换到新任务
}
}
  • 示例场景
    1. 发送任务 “BEGIN” -> 成功打印。
    2. 发送任务 “Slow” (耗时长) -> 紧接着发送 “Replace”。
    3. 结果:”Slow” 由于没跑完就被 “Replace” 顶替了,最终只打印了 “Replace”。

3. 开发者感悟

这是一个非常高级的 select 用法。它展示了协程如何通过“非阻塞选择”来实现复杂的流控制逻辑。在 Android 搜索功能中,如果用户连续输入关键词,我们可以通过这种机制确保只有最后一次输入的搜索结果被最终展示,中间由于输入过快产生的无效请求都会被忽略。

,