写一个 golang 风格的协程扩展

Posted Kotlin

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了写一个 golang 风格的协程扩展相关的知识,希望对你有一定的参考价值。

本文概要

asyncDeferredawaitawaitOrError
 
   
   
 
  1. launch {

  2.    val deferred =   ...

  3.    val (result, error) = deferred.awaitOrError()

  4.    if(error == null){

  5.        dealWithResult(result)    

  6.    } else {

  7.        handleError(error)

  8.    }

  9. }

需求的诞生

最近因为要定制 BatteryHistorian 这个框架的某些小功能,近距离接触了一些 golang,发现这门语言当中很多可能出异常的函数调用返回两个结果,例如:

 
   
   
 
  1. bytes, err := ioutil.ReadFile("Hello.go")

  2. if err == nil {

  3.    fmt.Print(string(bytes))

  4. } else {

  5.    fmt.Print(err)

  6. }

awaittry...catch
 
   
   
 
  1. try {

  2.    val deferred =   ...

  3.    val result = deferred.await()

  4.    dealWithResult(result)

  5. } catch (e: Exception) {

  6.    handleError(error)

  7. }

我当时想,如果 Kotlin 的协程能写出 golang 风格的返回,那体验起来还是很不错的。

返回多个值

可是刚要动手写,就要扑街了,Kotlin 不支持多个返回值哎,咋整?

Pair
 
   
   
 
  1. suspend fun <T> Deferred<T>.awaitOrError(): Pair<T, Throwable> {

  2.    return try {

  3.        await() to null

  4.    } catch (e: Exception) {

  5.        null to e

  6.    }

  7. }

可空类型的返回值

嗯,看上去不错,只是没法通过编译。为什么呢?返回结果的泛型参数需要定义为可空类型才可以。

 
   
   
 
  1. suspend fun <T> Deferred<T>.awaitOrError2(): Pair<T?, Throwable?> {

  2.    ...

  3. }

null

嗯,这回不仅看上去不错,编译也能通过了。不过,用起来却有点儿蛋疼。

 
   
   
 
  1. val (result, err) = async { ... }.awaitOrError()

resulterrresult
 
   
   
 
  1. if(err != null) {

  2.    if (result != null) {

  3.        dealWithResult(result)

  4.    }

  5. }

errresult

平台类型

awaitOrErrorresultnull
 
   
   
 
  1. T!

resultT!
Pair
 
   
   
 
  1. public class Result<T> {

  2.    private T result;

  3.    private Throwable error;

  4.    public T getResult() {

  5.        return result;

  6.    }

  7.    @Nullable

  8.    public Throwable getError() {

  9.        return error;

  10.    }

  11.    public static <T> Result<T> of(Throwable error) {

  12.        Result<T> result = new Result<T>();

  13.        result.error = error;

  14.        return result;

  15.    }

  16.    public static <T> Result<T> of(T result) {

  17.        Result<T> resultJava = new Result<T>();

  18.        resultJava.result = result;

  19.        return resultJava;

  20.    }

  21. }

getErrornullgetResult

Java 数据类与解构

只是,这时候又产生了新的问题,Java 中要怎么定义数据类呢?不是数据类又怎么解构呢?

Result
 
   
   
 
  1.    ...

  2.    public T component1() {

  3.        return result;

  4.    }

  5.    @Nullable

  6.    public Throwable component2() {

  7.        return error;

  8.    }

  9.    ...

componentNawaitOrError
 
   
   
 
  1. suspend fun <T> Deferred<T>.awaitOrError(): Result<T> {

  2.    return try {

  3.        Result.of(await())

  4.    } catch (e: Exception) {

  5.        Result.of(e)

  6.    }

  7. }

而在调用处,也能按照我们的意愿去检查错误,使用结果,就像文章开头提到的那样:

 
   
   
 
  1. launch {

  2.    val deferred =   ...

  3.    val (result, error) = deferred.awaitOrError()

  4.    if(error == null){

  5.        dealWithResult(result)    

  6.    } else {

  7.        handleError(error)

  8.    }

  9. }

resultT!

小结

try...catch...

以上是关于写一个 golang 风格的协程扩展的主要内容,如果未能解决你的问题,请参考以下文章