这是一个有趣的问题,您没有获得预期的异步行为的原因来自您的runBlocking
函数及其创建的上下文BlockingEventLoop
.当使用此调度程序运行协程时,您将仅在单个Thread
中工作.尽管从技术上讲,async
会异步启动协程,但对于您的例子来说,只有在执行像delay
这样的非阻塞操作时才会看到这种行为,这只是因为delay
调度,但这当然不是您要找的.您可能对让async
工作感兴趣的范围上下文是IO
、Default
或Unconfined
,但我认为Default
在您的例子中应用得最好.请记住,单元测试通常需要像runBlocking
或runTest
那样启动阻塞上下文,因此只有当您已经在协程作用域中(即在suspend
函数中或使用以runBlocking
开头的作用域)并且上下文已经是非阻塞时,使用coroutineScope
可能才有效.这里有3个不同版本的你自己的代码和3个不同的例子:一个是阻塞的,另一个是2种不同风格的非阻塞的.
@Test
fun `should xyz synchronously in spite of async`() {
runBlocking {
val x = async {
"x".also {
repeat(1000) {
println("waiting for network response.")
}
println("returning x")
}
}
val y = async {
"y".also { println("returning y") }
}
val z = async {
"z".also { println("returning z") }
}
println("x, y, z -> ${x.await()} , ${y.await()}, ${z.await()} ")
println(coroutineContext)
}
}
@Test
fun `should xyz asynchronously because of non-blocking with explicit Dispatchers_IO`() {
runBlocking {
withContext(Dispatchers.Default) {
val x = async {
"x".also {
repeat(1000) {
println("waiting for network response.")
}
println("returning x")
}
}
val y = async {
"y".also { println("returning y") }
}
val z = async {
"z".also { println("returning z") }
}
println("x, y, z -> ${x.await()} , ${y.await()}, ${z.await()} ")
println(coroutineContext)
}
}
}
@Test
fun `should xyz asynchronously because of non-blocking with coroutineScope`():Unit = runBlocking {
coroutineScope {
CoroutineScope(Dispatchers.Default).launch {
val x = async {
"x".also {
repeat(1000) {
println("waiting for network response.")
}
println("returning x")
}
}
val y = async {
"y".also { println("returning y") }
}
val z = async {
"z".also { println("returning z") }
}
println("x, y, z -> ${x.await()} , ${y.await()}, ${z.await()} ")
println(coroutineContext)
}.join()
}
}
请注意,正如@Joffrey所建议的那样,尽管这些示例用于明确Kotlin
协程中的contexts
和scopes
,但以下示例将是最正确的一个实现,以尊重称为structured concurrency
的协程中非常重要的概念.这是一个更复杂的概念来理解,但一般来说,我们应该避免上下文切换时,它是不必要的:
@Test
fun `should xyz asynchronously because of non-blocking with explicit Dispatchers_IO`() = runBlocking(Dispatchers.Default){
val x = async {
"x".also {
repeat(1000) {
println("waiting for network response.")
}
println("returning x")
}
}
val y = async {
"y".also { println("returning y") }
}
val z = async {
"z".also { println("returning z") }
}
println("x, y, z -> ${x.await()} , ${y.await()}, ${z.await()} ")
println(coroutineContext)
}
还要确保在这些测试中考虑使用断言.它们是不完整的.他们只专注于回答你的问题.