使用协成将异步转同步


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

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

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值