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

  • Learning Concurrency in Kotlin
  • Miguel Angel Castiblanco Torres
  • 511字
  • 2021-08-05 10:46:47

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.
主站蜘蛛池模板: 清苑县| 杨浦区| 班玛县| 杭州市| 栾川县| 兴义市| 澎湖县| 怀柔区| 合江县| 正安县| 临城县| 同江市| 沈丘县| 兰考县| 伊春市| 玛沁县| 长沙县| 龙江县| 嘉峪关市| 鲁甸县| 西乌珠穆沁旗| 龙门县| 兰溪市| 西乌| 灵宝市| 清河县| 扬州市| 轮台县| 抚州市| 莒南县| 林西县| 望谟县| 迁安市| 虞城县| 定边县| 克什克腾旗| 绥德县| 山东| 喜德县| 深水埗区| 宜宾市|