Kotlin遇到一个任务由多个接口才能完成时,如何优雅的执行

2,233 阅读2分钟

前言

不想华丽胡哨的写了,直接说问题和处理办法。

今天遇到一个需求,举个相同的例子说明,就是有两个接口A、B,A是获取某一段范围内用户的姓名列表,接口B中是根据用户的姓名查找这个用户的爱好,那么要通过ListView展示A接口+B接口中的数据,如何优雅的写?

image.png

肯定要先调用A接口吧,然后循环调用B接口?,B接口的请求回调中关联上指定的姓名,然后在设置适配器。

不同的人肯定有不同的写法,所以直接上我认为比较优雅的方法吧。

import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletionService
import java.util.concurrent.ExecutorCompletionService
import java.util.concurrent.ExecutorService
import java.util.function.Supplier
import java.util.stream.Collector
import java.util.stream.Collectors
import java.util.stream.Stream

fun syncPost(name: String): String {
    return "爱好${(0..10).random()}"
}

var map = mutableMapOf<String, String>()
fun getHobby(name: String, map: MutableMap<String, String>): CompletableFuture<Unit> {
    return CompletableFuture.supplyAsync {
        Thread.sleep((0..1000).random().toLong())
        map[name] = syncPost(name)
    }
}

fun main() {
    var listName = Stream.generate { "李${(0..10).random()}" }.limit(10).collect(Collectors.toSet())
    var exec = mutableListOf<CompletableFuture<Unit>>()
    println(map)
    listName.forEach {
        exec.add(getHobby(it, map))
    }
    var allOf = CompletableFuture.allOf(*exec.toTypedArray()).thenAccept {
        println("执行完成")
        println(map)
    }
    allOf.get()

}

原理还是利用了CompletableFuture,首先 CompletableFuture是Future的扩展,意味着更强,并且解决了Future不能完成的事情,如本例就用到了Future所没有的功能,也就是将多个异步任务一起执行。

还有个更方便的方法,CompletableFuture提供了一个回调功能,这个回调是在所有任务都完成后被调用,也就是thenAccept方法,如果通过循环调用请求,如何判断所有的请求都执行完毕?是不是需要一个变量累加或是别的方法,但是感觉还有没有thenAccept方便吧。

同样我们可以指定超时时间,时间到达后如果还没有全部完成,则抛出TimeoutException。

try {
    allOf.get(500, TimeUnit.MILLISECONDS)
} catch (ep: TimeoutException) {
    ep.printStackTrace()
}

另外如果在任务中异常没有捕获,那么并不会影响其他的任务,但是不会执行thenAccept,需要注意的是,如果在任务中抛出异常,并不会立马输出,而是在全部任务结束后才被真正抛出。

fun getHobby(name: String, map: MutableMap<String, String>): CompletableFuture<Unit> {
    return CompletableFuture.supplyAsync {
        Thread.sleep((0..1000).random().toLong())
        map[name] = syncPost(name)
        if (name == "李2")
            throw  NullPointerException("null")
        println("${Thread.currentThread().name}执行完毕")

    }
}

如果某个任务失败,还需要把他加入到一个重试队列中。