使用协成将异步转同步


最近学习了一下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) {
    
    

    //增加suspend方法
    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)
}

从执行结果可以看出,任务为并发执行
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/hbdatouerzi/article/details/123880429