最近学习了一下kotlin的协成,发现使用协成能够帮助我们将异步代码转换成同步代码,能够极大的提高代码的可读性。
1.普通的异步写法
如下所示,为我们常见的一些异步代码,通常情况下,我们会传递一个callback到方法里面,等到异步代码执行完毕之后回调回来。
public fun realRequest(response: Callback) {
Thread(Runnable {
println("start request:" + address)
Thread.sleep(1000)
if (address.equals("failed")) {
response.onError(0, "address failed")
} else {
response.onResult("success:" + address)
}
}).start()
}
我们调用这段代码如下
Request("test").realRequest(object: Callback {
override fun onResult(result: String) {
println("success!")
}
override fun onError(errorCode: Int, errorMsg: String) {
println("errorcode:${errorCode},errorMsg:${errorMsg}")
}
})
我们从这段代码里面可以看出,Request(“test”)前后的代码和回调方法里面的代码割裂开了,增加了阅读成本,另外如果回调方法特别长,阅读起来就会特别费劲。 那么我们可以尝试着用协成的方法来改写这段代码。
2.使用协成改写
我们可以在Request类里面增加一个suspend方法,将realRuest转变成同步方法,具体如下
class Request(val address:String) {
public suspend fun request(): TestResult {
return suspendCoroutine { continuation ->
realRequest(object : Callback {
override fun onResult(result: String) {
continuation.resume(TestResult(true,result))
}
override fun onError(errorCode: Int, errorMsg: String) {
continuation.resume(TestResult(false,errorMsg,errorCode))
}
})
}
}
public fun realRequest(response: Callback) {
Thread(Runnable {
println("start request:" + address)
Thread.sleep(1000)
if (address.equals("failed")) {
response.onError(0, "address failed")
} else {
response.onResult("success:" + address)
}
}).start()
}
}
具体使用如下,在协成当中访问就可以了
GlobalScope.launch {
val result = Request("test").request()
println(result.toString())
}
如果我们引用的是一个库,不方便更改Request源码,那么也可以使用扩展的方式来实现, 如下所示,我们在Request外部给其增加了一个扩展方法outRequest
suspend fun Request.outRequest():TestResult{
return suspendCoroutine { continuation ->
this.realRequest(object :Callback{
override fun onResult(result: String) {
continuation.resume(TestResult(true,result))
}
override fun onError(errorCode: Int, errorMsg: String) {
continuation.resume(TestResult(false,errorMsg,errorCode))
}
})
}
}
使用方式也是完全一致
GlobalScope.launch {
val result = Request("outTest").outRequest()
println(result.toString())
}
3.多线程并发
如果我们想要发起多个请求,可以使用flow。
GlobalScope.launch {
val start = System.currentTimeMillis()
val r1 = Request("test1")
val r2 = Request("test2")
val r3 = Request("test3")
val list = listOf(r1,r2,r3)
flow {
for (request in list) {
val result = request.request()
emit(result)
}
}.collect {
println(it.toString())
}
val gap = System.currentTimeMillis() - start
println("cost time:"+gap)
}
如上面代码所示,我们有三个异步请求,可以将其放到flow当中,分别进行emit,然后在collect当中获取结果。 每个异步任务执行时间是1秒,从结果当中可以发现,整体执行时间为3秒,可以看出所有的任务都是串行执行。 如果我们希望任务能够并发执行呢,可以使用channelFlow
GlobalScope.launch {
val start = System.currentTimeMillis()
val r1 = Request("test1")
val r2 = Request("test2")
val r3 = Request("test3")
val list = listOf(r1,r2,r3)
channelFlow {
for (request in list){
async {
val result = request.request()
send(result)
}
}
}.collect {
println(it.toString())
}
val gap = System.currentTimeMillis() - start
println("cost time:"+gap)
}
从执行结果可以看出,任务为并发执行
|