如果没有默认值,为什么可以在不提供 CoroutineContext 的情况下调用 runBlocking?
Why can `runBlocking` be invoked without providing a CoroutineContext, if there's no default value for it?
我总是检查我使用的东西的实现。
目前我使用的注入库不支持可暂停函数 (Koin),因此,仅(即使不鼓励)引导应用程序,我有时会使用 runBlocking。
为了拥有更丰富的日志,我用一些信息丰富了协程上下文,但在大多数上下文更改(launch
、async
和 runBlocking
等等)。
特别是,鉴于非挂起方法无法访问 CoroutineContext,我非常好奇 runBlocking
从哪里获取它。
您可以像这样使用runBlocking
:
runBlocking {...}
然而,当我检查它的实现时,它有两个参数:a CoroutineContext
和要执行的挂起块。 None个参数有默认值,为什么不传就可以调用呢?我真的不明白!
此外,页面上说默认值为 EmptyCoroutineContext
但代码文档说了一些关于事件循环的内容。
那我再问一遍?为什么我可以在不传递值的情况下调用它,实际默认值是多少?
默认情况下 runBlocking()
从一个空协程上下文开始。
context
没有默认值这一事实确实令人困惑和奇怪。我认为(但我不是 100% 确定)这是因为通过 ctrl+clicking on runBlocking()
我们去实施,所以 actual
定义。但是代码是根据 expect
声明编译的。我没有找到直接在 IntelliJ 中查看 expect
声明的简单方法,但可以找到 here:
public expect fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T
我们可以看到,这个声明中的context
有一个默认值。不过,这在使用 IntelliJ 时确实令人困惑。
关于提到的事件循环:是的,runBlocking()
创建(或 re-uses)一个事件循环,但我看不出它与协程上下文有什么关系。
请记住,您传递给 runBlocking
或 launch
等协程构建器的上下文 而不是 在协程中实际可用的上下文,但它的 父级 。构建器添加自己的项目并将它们合并到您提供的上下文中。
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
(协程调度器属于更广泛的延续拦截器类别)
你问题的另一部分,为什么你不必传入看似 non-default 的参数,在别处得到了回答。基本上,它是 IDE 和 expected
与 actual
声明的产物。相关的声明是 expected
,actual
是一个实现细节,但是 IDE 带你到那一个。
我总是检查我使用的东西的实现。
目前我使用的注入库不支持可暂停函数 (Koin),因此,仅(即使不鼓励)引导应用程序,我有时会使用 runBlocking。
为了拥有更丰富的日志,我用一些信息丰富了协程上下文,但在大多数上下文更改(launch
、async
和 runBlocking
等等)。
特别是,鉴于非挂起方法无法访问 CoroutineContext,我非常好奇 runBlocking
从哪里获取它。
您可以像这样使用runBlocking
:
runBlocking {...}
然而,当我检查它的实现时,它有两个参数:a CoroutineContext
和要执行的挂起块。 None个参数有默认值,为什么不传就可以调用呢?我真的不明白!
此外,页面上说默认值为 EmptyCoroutineContext
但代码文档说了一些关于事件循环的内容。
那我再问一遍?为什么我可以在不传递值的情况下调用它,实际默认值是多少?
默认情况下 runBlocking()
从一个空协程上下文开始。
context
没有默认值这一事实确实令人困惑和奇怪。我认为(但我不是 100% 确定)这是因为通过 ctrl+clicking on runBlocking()
我们去实施,所以 actual
定义。但是代码是根据 expect
声明编译的。我没有找到直接在 IntelliJ 中查看 expect
声明的简单方法,但可以找到 here:
public expect fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T
我们可以看到,这个声明中的context
有一个默认值。不过,这在使用 IntelliJ 时确实令人困惑。
关于提到的事件循环:是的,runBlocking()
创建(或 re-uses)一个事件循环,但我看不出它与协程上下文有什么关系。
请记住,您传递给 runBlocking
或 launch
等协程构建器的上下文 而不是 在协程中实际可用的上下文,但它的 父级 。构建器添加自己的项目并将它们合并到您提供的上下文中。
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
(协程调度器属于更广泛的延续拦截器类别)
你问题的另一部分,为什么你不必传入看似 non-default 的参数,在别处得到了回答。基本上,它是 IDE 和 expected
与 actual
声明的产物。相关的声明是 expected
,actual
是一个实现细节,但是 IDE 带你到那一个。