suspend
가 붙은 함수suspend
함수를 호출할 수 있다.main
함수에서 suspend
함수를 사용할 수 있었던건 launch
등의 코루틴 빌더의 마지막 파라미터가 suspending lambda
이기 때문이다.fun main(): Unit = runBlocking {
launch {
delay(100L)
}
}
// launch의 시그니처
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
main
에서 suspend
함수인 delay
를 호출할 때 main
이 suspend
함수일 필요는 없고 delay
를 실행시키는 block
파라미터가 suspend
함수였던 것suspend
함수는 코루틴이 중지 되었다가 재개 될 수 있는 지점이다.
suspend
함수를 활용하면 여러 비동기 라이브러리를 사용할 수 있도록 도와준다.async
와 Deffered
(async
의 반환 값)를 사용해 콜백 없이 코드를 작성했다.fun main(): Unit = runBlocking {
val result1 = async {
apiCall1()
}
// val result1: Defferred<Int>
val result2 = async {
apiCall2(result1.await())
}
printWithThread(result2.await())
}
// apiCall1, apiCall2를 외부 라이브러리라 가정 (1초 걸림)
fun apiCall1(): Int {
Thread.sleep(1_000L)
return 100
}
fun apiCall2(num: Int): Int {
Thread.sleep(1_000L)
return num * 2
}
CompletableFuture
나 Reactor
같은 다른 라이브러리를 사용해야 한다면 변경이 main
에 전파된다는 점이다.
suspend
함수를 활용하면 이 문제를 개선할 수 있다.fun main(): Unit = runBlocking {
val result1 = apiCall1()
val result2 = apiCall2(result1)
printWithThread(result2)
}
suspend fun apiCall1(): Int {
return CoroutineScope(Dispatchers.Default).async {
Thread.sleep(1_000L)
100
}.await()
}
suspend fun apiCall2(num: Int): Int { // 여기선 CompletableFuture 사용
return CompletableFuture.supplyAsync {
Thread.sleep(1_000L)
num * 2
}.await() // 이 await는 코루틴이 만들어 놓은 확장함수이다.
}
apiCall1
, apiCall2
를 suspend
함수로 변경해 내부에서 어떤 비동기 라이브러리를 사용하던 해당 함수의 선택으로 남겨둔 것coroutineScope
launch
나 async
로 만들어진 코루틴은 바로 실행되지 않는다.fun main(): Unit = runBlocking {
printWithThread("START")
printWithThread(calculateResult())
printWithThread("END")
}
suspend fun calculateResult(): Int = coroutineScope {
val num1 = async {
delay(1_000L)
10
}
val num2 = async {
delay(1_000L)
20
}
num1.await() + num2.await()
}
// 출력 결과 (launch 등과 달리 바로 실행되기에 30이 END 전에 출력된다.)
// START
// 30
// END
withContext
coroutineScope
와 기본적으로 유사하다context
에 변화를 줄 수 있다.Dispatcher
를 바꿔 사용하는 예제다.fun main(): Unit = runBlocking {
printWithThread("START")
printWithThread(calculateResult())
printWithThread("END")
}
suspend fun calculateResult(): Int = withContext(Dispatchers.Default) {
val num1 = async {
delay(1_000L)
10
}
val num2 = async {
delay(1_000L)
20
}
num1.await() + num2.await()
}
// 출력 결과
// START
// 30
// END
withTimeout
withTimeoutOrNull
coroutineScope
와 기본적으로 유사withTimeout
은 예외를 던진다.withTimeoutOrNull
은 null
을 반환한다.fun main(): Unit = runBlocking {
withTimeout(1_000L) {
delay(1_500L)
10 + 20
}
}
// 출력 결과
// Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1000 ms