// Incorrect: blocking call in a suspending functionsuspendfungetUser():User{valresponse=api.getUser()// blocking callreturnresponse.toDomainUser()}// Correct: Using withContext(Dispatchers.IO) to make a blocking call in a suspending functionsuspendfungetUser():User=withContext(Dispatchers.IO){valresponse=api.getUser()// blocking callresponse.toDomainUser()}
funmain(){log("Starting main")runBlocking{log("Starting first runBlocking")delay(1000)log("Finishing first runBlocking")}valresult:String=runBlocking{log("Starting second runBlocking")delay(1000)"ABCD"}log("Second runBlocking finished with result: $result")}funlog(message:String){println("[${Thread.currentThread().name}] $message")}// [main] Starting main// [main] Starting first runBlocking// (1 sec)// [main] Finishing first runBlocking// [main] Starting second runBlocking// (1 sec)// [main] Second runBlocking finished with result: ABCD
由于 runBlocking 启动了一个作用域,它会等待其中启动的所有协程完成。这意味着它会等待所有子协程完成。这就是为什么下面的程序要等到所有三个异步协程都完成才会完成。为了展示如何使用 runBlocking 定义结果,我还让这个程序从 main 函数返回 0。
12345678910111213141516171819
importkotlinx.coroutines.*funmain():Int=runBlocking{launch{delayAndPrintHello()}launch{delayAndPrintHello()}launch{delayAndPrintHello()}println("Hello")0// result from main}suspendfundelayAndPrintHello(){delay(1000L)println("World!")}// Hello// (1 sec)// World!// World!// World!
classUserService(privatevaluserRepository:UserRepository,){suspendfunfindUserById(id:String):User=userRepository.findUserById(id)// Blocking alternative for legacy parts of our applicationfunfindUserByIdBlocking(id:String):User=runBlocking{findUserById(id)}// ...}
// Incorrect: runBlocking in a suspending functionsuspendfungetToken()=runBlocking{// ...}// runBlocking is most likely not neededsuspendfungetToken(){// ...}
// Incorrect: runBlocking used where we do not need to await resultfunstartBackgroundProcess()=runBlocking{doSomething()}// Correct: Using launch to start an asynchronous coroutinefunstartBackgroundProcess(){backgroundScope.launch{doSomething()}}
// Incorrect: runBlocking on the main threadfunonClick()=runBlocking{userNameView.test=getUserName()}// Correct: Using launch to start an asynchronous coroutinefunonClick(){lifecycleScope.launch{userNameView.test=getUserName()}}