如果没有默认值,为什么可以在不提供 CoroutineContext 的情况下调用 'runBlocking'?



我总是检查我使用的东西的实现情况。

目前,我使用的注入库不支持可挂起的函数(Koin(,因此,只有在引导应用程序时(即使不鼓励(,我有时会使用runBlocking。

为了拥有更丰富的日志,我正在用一些信息丰富协程上下文,但这些信息在大多数上下文更改中都会丢失(launchasyncrunBlocking等(。

特别是,考虑到非挂起方法不能访问CoroutineContext这一事实,我非常好奇runBlocking从哪里获得它。

你可以像一样使用runBlocking

runBlocking {...}

然而,当我检查它的实现时,它有两个参数:CoroutineContext和要执行的挂起块。这些参数都没有默认值,那么为什么我可以在不传递它的情况下调用它呢?我真的不明白!

此外,该页面显示默认值为EmptyCoroutineContext,但代码文档显示了一些关于事件循环的内容。

所以我又问了一遍?为什么我可以在不传递值的情况下调用它,实际默认值是什么?

默认情况下,runBlocking()以一个空的协程上下文开始。

context没有默认值这一事实确实令人困惑和奇怪。我认为(但我不能100%确定(这是因为通过ctrl+点击runBlocking(),我们就可以实现actual了。但是代码是根据expect声明编译的。我没有找到一种直接在IntelliJ中查看expect声明的简单方法,但可以在这里找到:

public expect fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T

正如我们所看到的,这个声明中的context有一个默认值。不过,在使用IntelliJ时,这确实令人困惑。

关于提到的事件循环:是的,runBlocking()创建(或重新使用(了一个事件循环,但我不知道它与协同程序上下文有什么关系。

请记住,传递给像runBlockinglaunch这样的协程生成器的上下文是,而不是实际在协程中可用的上下文,而是它的父级。构建器添加自己的项,并将它们合并到您提供的上下文中。

runBlocking使用其自己的调度器,该调度器仅在runBlocking函数调用的生命周期内存在。您可以在runBlocking主体内的协同程序上下文中找到这个调度器,例如使用以下代码:

import kotlinx.coroutines.runBlocking
fun main() {
runBlocking {
val ctxElems = coroutineContext.fold(mutableListOf<Pair<Any, Any>>()) { list, element ->
list.also { it.add(element.key to element) }
}
for ((key, value) in ctxElems) {
println("${key::class.qualifiedName}: $value")
}
}
}

这将打印

kotlinx.coroutines.CoroutineId.Key: CoroutineId(1)
kotlinx.coroutines.Job.Key: "coroutine#1":BlockingCoroutine{Active}@12843fce
kotlin.coroutines.ContinuationInterceptor.Key: BlockingEventLoop@3dd3bcd

(协同调度器属于更广泛的连续拦截器类别(

问题的另一部分,为什么不必传入看似非默认的参数,在其他地方得到了答案。基本上,它是IDE和expectedactual声明的产物。相关的声明是expected声明,actual是一个实现细节,但是IDE会将您带到那个声明。

相关内容

  • 没有找到相关文章

最新更新