你会怎么实现下面这个场景?应用首页有三个优先级从高到低的弹窗,展示内容依赖网络请求,若低优先级弹窗请求先返回则需等待,让高优先级先展示。
串行请求是最容易想到的解决方案,即先请求最高优先级的弹窗,当它返回展示后再请求第二优先级弹窗。但这样会拉长所有弹窗的展示时间。
性能更好的方案是同时并行发出三个请求,但网络请求时长的不确定性使得最高优先级的弹窗不一定优先返回,所以得设计一种优先级阻塞机制。本文使用 协程 + 链式队列 实现这个机制。
异步任务链
把单个异步任务进行抽象:
// 单个异步任务
class Item {
companion object {
// 默认异步优先级
const val PRIORITY_DEFAULT = 0
}
// 异步操作:耗时的异步操作
var suspendAction: (suspend () -> Any?)? = null
set(value) {
field = value
value?.let {
// 启动协程执行异步操作
GlobalScope.launch { deferred = async { it.invoke() } }
}
}
// 异步响应:异步操作结束后要做的事情
var resumeAction: ((Any?) -> Unit)? = null
// 异步结果:异步操作返回值
var deferred: Deferred? = null
// 异步优先级
var priority: Int = PRIORITY_DEFAULT
}
复制代码
异步任务有三个主要的属性,分别是异步操作suspendAction、异步响应resumeAction、异步结果deferred。每当异步操作被赋值时,就启动协程执行它,并将其返回值保存在deferred中。
为了确保异步任务的优先级,把多个异步任务用链的方式串起来,组成异步任务链:
class Item {
companion object {
const val PRIORITY_DEFAULT = 0
}
var suspendAction: (suspend () -> Any?)? = null
set(value) {
field = value
value?.let {
GlobalScope.launch { deferred = async { it.invoke() } }
}
}
var resumeAction: ((Any?) -> Unit)? = null
var deferred: Deferred? = null
var priority: Int = PRIORITY_DEFAULT
// 异步任务前结点(Item 包含 Item)
internal var next: Item? = null
// 异步任务后结点(Item 包含 Item)
internal var pre: Item? = null
// 在当前结点后插入新结点
internal fun addNext(item: Item) {
next?.let {
it.pre = item
item.next = it
item.pre = t