1.2.1 结果传递

不同于同步调用,由于异步调用是立即返回的,因此被调方的逻辑通常存在两种情形:

·结果尚未就绪,进入任务执行的状态,待结果就绪后通过回调传递给调用方;

·结果已经就绪,可以立即提供结果。

两种情况如图1-3所示,见代码清单1-5。

代码清单1-5 异步回调返回结果的两条路径示意


fun asyncBitmap(
  url: String,
  callback: (Bitmap) -> Unit
): Bitmap? {
  return when (val bitmap = Cache.get(url)) {
    null -> {
      thread {
        download(url)
          .also { Cache.put(url, it) }
          .also(callback)
      }
      null
    }
    else -> bitmap
  }
}

图1-3 异步回调示意图

asyncBitmap函数的逻辑结果是Bitmap类型,如果结果已经存在,会直接将结果返回,否则返回null,通知主流程开始执行异步任务,等异步操作结束之后再通过回调来返回结果。调用它的代码见代码清单1-6。

代码清单1-6 调用异步函数asyncBitmap


val bitmap = asyncBitmap("...") {
  show(it) // ... ② 异步请求
}

if (bitmap != null) {
  show(bitmap) // ... ① 直接返回
}

代码清单1-6中的序号与图1-3对应,其中①对应Nonsuspended分支,②对应Suspended分支,我们可以确保程序总是可以沿着①或者②当中的一条路径执行。

当然,通常我们不会如此设计回调API,因为这样反而让程序写起来更复杂了。更为常见的做法是,在结果就绪的情况下仍然立即以回调的形式传递给调用方,以保证结果传递方式的透明性。

不过,如果能够借助编译器的手段来简化这段逻辑的编写,本节提供的这个思路会非常有用。

提示 Kotlin协程的挂起函数(suspend function)本质上就采取了这个异步返回值的设计思路,详情见3.2.3节。