async로 비동기 작업을 정의한 뒤, 아무것도 하지 않은 채 연산이 완료되는 걸 기다리는 건 아무 의미가 없습니다.
// X
suspend fun getUser(): User = coroutineScope {
val user = async { repo.getUser() }.await() // 동기 코드
user.toUser()
}
// O
suspend fun getUser(): User {
val user = repo.getUser()
return user.toUser()
}
비동기 작업 여러 개를 동시에 수행한다면, 마지막 작업을 제외한 모든 작업이 async를 사용해야 합니다. 가독성을 위해 모든 작업에 async를 사용하는 것이 낫습니다.
...
val configDeferred = async { ... }
val newsDeferred = async { ... }
val userDeferred = async { ... }
view.showNews(user.await(), news.await())
coroutineScope
를 사용하세요withContext와 coroutineScope의 차이는 withContext가 컨텍스트를 재정의 할 수 있다는 것밖에 없으므로 coroutineScope를 사용하세요.
// 컨텍스트 재정의, 기존 코루틴의 컨텍스트 변경
suspend fun example() {
withContext(Dispatchers.IO) {
// IO 스레드에서 실행
val data = fetchData()
data
}
}
// 새로운 코루틴 스코프 생성 (Structured Concurrency 보장)
suspend fun example() {
coroutineScope {
// 새로운 코루틴 스코프 생성
launch { doSomething() }
async { fetchData() }
}
}
awaitAll
을 사용하세요suspend fun getUsersData(): List<User> = coroutineScope {
val deferreds = listOf(
async { userService.getUser(1) },
async { userService.getUser(2) },
async { userService.getUser(3) }
)
deferreds.awaitAll()
}
중단 함수를 호출할 때, 현재 스레드가 블로킹될까 봐 걱정하면 안 됩니다. 특히 Dispatchers.Main을 자주 사용하는 안드로이드에서 중요하지만, 백엔드에서 동기화를 위해 싱글스레드로 제한된 디스패처를 사용하는 경우에도 마찬가지입니다.
중단 함수가 블로킹 함수를 호출할 때는 Dispatchers.IO나 블로킹에 사용하기로 설계된 커스텀 디스패처를 사용해야 합니다.