官术网_书友最值得收藏!

Starting a coroutine with async

When a coroutine is started with the intention of processing its result, async() must be used. It will return a Deferred<T>, where Deferred is a non-blocking cancellable future – provided by the coroutines' framework – and T is the type of the result.

When using async, you must not forget to process its result – which can easily happen, so be wary.

Consider the following code:

fun main(args: Array<String>) = runBlocking {
val task = async {
doSomething()
}
task.join()
println("Completed")
}

Here, doSomething() is simply throwing an exception:

fun doSomething() {
throw UnsupportedOperationException("Can't do")
}

You may be tempted to assume that this will stop the execution of the application, the stack of the exception will be printed, and the app's exit code will be different from zero – zero means that no error happened, anything different means error. Let's see what happens when this is executed:

As you can see, there is no exception stack trace being printed in logs, the application didn't crash, and the exit code indicates successful execution.

This is because any exception happening inside an async() block will be attached to its result, and only by checking there you will find the exception. For this, the isCancelled and getCancellationException() methods can be used together to safely retrieve the exception. The previous example can be updated so that it validates the exception, like shown here:

fun main(args: Array<String>) = runBlocking {
val task = async {
doSomething()
}
task.join()
if (task.isCancelled) {
val exception = task.getCancellationException()
println("Error with message: ${exception.message}")
} else {
println("Success")
}
}
At the time of writing, there is an effort from the Kotlin team to make the code above work as explained here. If you notice that isCancelled is returning false in the scenario above, please replace it with isCompletedExceptionally. I decided to avoid mentioning isCompletedExceptionally because it's being deprecated close to the release of the book. For more information, please read issue 220 in the coroutines' GitHub repository.

This will then print the following error:

In order to propagate the exception,  await() can be called on Deferred, for example:

fun main(args: Array<String>) = runBlocking {
val task = async {
doSomething()
}
task.await()
println("Completed")
}

This will crash the application:

This crash happens because by calling await(), we are unwrapping Deferred, which in this case will unwrap to the exception and propagate it.

The main difference between waiting with join() and then validating and processing any error, and calling await() directly, is that in the first option we are handling the exception without having to propagate it, whereas by just calling await() the exception will be propagated.

For this reason, the example using await() will return the code one, meaning an error during the execution, whereas waiting with join() and using isCancelled and getCancellationException() to handle the error will result in a successful code of zero.

In Chapter 3, Life Cycle and Error Handling, we will talk more about proper exception handling and propagation depending on the expected behavior.
主站蜘蛛池模板: 浠水县| 嵊州市| 渝北区| 麟游县| 新邵县| 山阴县| 油尖旺区| 嵊州市| 贺兰县| 鞍山市| 阿尔山市| 萨迦县| 通辽市| 祁门县| 逊克县| 阿拉善左旗| 壤塘县| 谷城县| 长葛市| 吴川市| 滕州市| 定边县| 云南省| 滨州市| 江达县| 罗江县| 遂昌县| 灵寿县| 新巴尔虎右旗| 平度市| 德阳市| 德昌县| 琼海市| 新密市| 竹北市| 门源| 红桥区| 泽库县| 洛川县| 凭祥市| 宝坻区|