최데브는 오늘도 프로그래밍을 한다.

코루틴의 job 과 Deferred 의 차이 본문

Android/Coroutine

코루틴의 job 과 Deferred 의 차이

최데브 2022. 3. 26. 12:02
반응형

코투린의 비동기 실행을 하다보면 

코루틴을 시작할때 launch 를 사용하거나 async 를 사용하거나 하는데

동작은 비슷하게 하지만 둘의 차이가 있다.

 

launch - 새로운 코루틴을 시작한다. 결과값을 전달하지 않을 때 사용한다.

async - 결과를 리턴할 수 있는 코루틴을 시작할때만 사용한다.

 

둘의 차이는 리턴값이 있냐 없냐의 차이다.

뭘 사용하는가에 따라 메소드나 완료 대기에 대한 대응이 달라진다.

이 이야기를 한건 launch 을 사용하면 job 을 반환하고 async 를 사용하면 Deferred 를 반환하기 때문이다.

 

 


 

그럼 다시 제목의 내용으로 돌아와서 job 과 Deferred 에 대해 알아보자.

 

사실은 job 과 Deferred 는 비슷하다. 그도 그럴게 Deferred 는 job을 확장하는 인터페이스로 만들어져있기 떄문이다.

Deferred 는 job 의 모든 특성을 가지고 있다.

 

async 를 사용하고 결과값을 수신하기 위해 await()를 사용하면 Deferred 를 반환하게 된다.

suspend fun main(){
	val deferred : Deferred<String> = 
    CoroutineScope(Dispatchers.IO).async{
    	"Deferred Result"
    }
    
    val deferredResult = deferred.await()//deferred에서 결과 올때까지 pause
    
    println(deferredResult)
}

 

코드로 보면 이렇다.

별도의 딜레이가 없어도 IO 스레드에서 수신 받기까지 main이 일시중단 되기 때문에 main 이 먼저 중단되지 않는다.

 

그럼 이제 기본 배경지식을 알아봤으니 본격적으로 job 과 Deferred 의 다른점에 대해 알아보자.

 

예외가 자동으로 전파되는 job과는 달리 Deferred 는 예외를 자동으로 전파하지 않는다.

이는 Deferred가 결과값 수신을 대기해야 하기 때문이다. 

 

suspend fun main(){

	val exceptionHandler = CoroutineExceptionHandler {_, exception ->
    	when(exception){
        	is IllegalArgumentException -> println("error1")
            is InterruptedExcetpion -> println("error2")
        }
    }
    
    val deferred = CoruntineScope(Dispatcher.IO).async(exceptionHandler){
    	throw IllegalArgumentException()
        arrayOf(1,2,3)
    }
    
    delay(1000)
}

이 코드를 실행하면 뭐라도 실행창에 나올 것 같지만 아무것도 나오지 않는다.

왜냐하면 위에 말한 것 처럼 Deferred 는 자동으로 에러를 전파하지 않기 때문이다.

Deferred 는 await()를 사용해서 미래 시점에 값을 받는데 해당 값이 필요없는 상황에서는 에러를 전파 시킬 필요가 없기 때문이다.

 

위 코드에 delay 부분을 지우고 deferred.await()로 바꾸면 이제는 해당 값이 필요하게 됐으므로 에러를 발생시킨다.

 

suspend fun main(){

	val exceptionHandler = CoroutineExceptionHandler {_, exception ->
    	when(exception){
        	is IllegalArgumentException -> println("error1")
            is InterruptedExcetpion -> println("error2")
        }
    }
    
    val deferred = CoruntineScope(Dispatcher.IO).async(exceptionHandler){
    	throw IllegalArgumentException()
        arrayOf(1,2,3)
    }
    
    deferred.await()
}

 

 

하지만 위 deferred.await() 를 사용하여 실행한 코드는 문제가 있다. 에러를 처리할때 핸들러에서 처리하는게 아니라 

main 스레드에서 에러가 전파되어서 main 스레드가 종료되어버리는 문제가 있다.

 

suspend fun main(){

	val exceptionHandler = CoroutineExceptionHandler {_, exception ->
    	when(exception){
        	is IllegalArgumentException -> println("error1")
            is InterruptedExcetpion -> println("error2")
        }
    }
    
    val deferred = CoruntineScope(Dispatcher.IO).async{
    	throw IllegalArgumentException()
        arrayOf(1,2,3)
    }
    
    CoruntineScope(Dispatcher.IO).launch(exceptionHandler){
    	 deferred.await()
    }.join()
   
}

이렇게 deferred.await() 로 에러가 전파될 수 있는 위치에 핸들러를 넣어주면 main 스레드가

종료되지 않고 에러 핸들링을 하게 된다.

 

요약하자면  launch() 함수로 정의된 코루틴 블록은 즉시 수행되며,

반환 받은 Job 객체는 해당 블록을 제어는 할수 있지만 코루틴 블록의 결과를 반환하지는 않는다.

코루틴 블록의 결과 값을 반환받고 싶다면 async() 코루틴 블록을 생성한다 job객체와 동일하게 블록제어도 가능하다. 그리고 await()로 해당 값을 받을 수 있다.

이러한 결과값을 받고 안받고의 차이로 인해 에러가 전파되는 방식이 다르다.

 

 

반응형
Comments