协程系列(四)Composing suspending functions

297 阅读1分钟

本文基于协程官方文档讲解,具体可查看here

一、顺序执行的挂起函数

定义两个挂起函数,在同一个协程体里面执行

suspend fun doSomeOne(): Int {
    delay(1000)
    return 12;
}
suspend fun doSometwo(): Int {
    delay(2000)
    return 22;
}
fun main() {
    runBlocking(Dispatchers.IO) {
        val time = measureTimeMillis {
            doSomeOne()
            doSometwo()
        }
        printMsg("耗时$time")
    }
}

image.png kt的语法糖measureTimeMillis测试耗时很方便。

1.1、使用async await来并发处理下

fun main()= runBlocking {
    val time= measureTimeMillis {
        val one= async { doSomeOne() }
        val two=async { doSometwo() }
        printMsg("${one.await()} ${two.await()}")
    }
    printMsg("耗时$time")
}

image.png

如果你不小心写成下面👇这样,就不一样结果了.串行执行了。

fun main() = runBlocking {
    val time = measureTimeMillis {
        val one = async { doSomeOne() }.await()
        val two = async { doSometwo() }.await()
    }
    printMsg("耗时$time")
}

image.png

1.2、async支持懒加载的启动模式

fun main() = runBlocking {
    val time = measureTimeMillis {
        val one = async(start=CoroutineStart.LAZY) { doSomeOne() }
        val two = async(start=CoroutineStart.LAZY) { doSometwo() }
        printMsg("${one.await()}     ${two.await()} ")
    }
    printMsg("耗时$time")
}

image.png

这里的懒加载就成了串行执行。 当懒加载遇上start()方法,就又可以并发执行了。

fun main()= runBlocking {
    val time= measureTimeMillis {
        val one= async(start=CoroutineStart.LAZY) { doSomeOne() }
        val two=async(start=CoroutineStart.LAZY) { doSometwo() }
        one.start()  
        two.start()
        printMsg("${one.await()}     ${two.await()} ")
    }
    printMsg("耗时$time")
}

image.png

1.3、GlobalScope.async不是挂起函数,但可调用挂起函数

fun someUsefulOneAsync()= GlobalScope.async {  //不是挂起函数
    doSomeOne()
}
fun someUsefulTwoAsync()= GlobalScope.async { //不是挂起函数
    doSometwo()
}
suspend fun doSomeOne():Int{ 
    delay(1000)
    return 12;
}
suspend fun doSometwo():Int{
    delay(2000)
    return 22;
}
fun main() = runBlocking {
  val job =  launch(Dispatchers.Default) {
        val time = measureTimeMillis {
            val one = someUsefulOneAsync()
            val two = someUsefulTwoAsync()
            printMsg("${one.await()}")
            printMsg("${two.await()}")
        }
        printMsg("$time")
    }
    printMsg("Done")
    delay(1200)
    job.cancel()
}

image.png 这里GlobalScope.async调用挂起函数,而且它可以正常地响应取消。

1.4 async结构化的并发

suspend fun concurrentSum():Int = coroutineScope {
    val one = async { doSomeOne() }
    val two = async { doSometwo() }
    one.await()+two.await()
}
suspend fun doSomeOne():Int{
    delay(1000)
    return 12;
}
suspend fun doSometwo():Int{
    delay(2000)
    return 22;
}
fun main()= runBlocking {
    launch(Dispatchers.Default) {
        val time = measureTimeMillis {
            printMsg("${concurrentSum()}")
        }
        printMsg("${time}")
    }
    printMsg("Done")
}

coroutineScope在这里就是保证让其block执行完才能走后面的。

image.png

1.4.1 async结构化的并发捕捉异常

suspend fun concurrentSum():Int= coroutineScope {
    val one = async {
        try{
            delay(10000L)
            43
        }finally {
            printMsg("first child was cancelled")
        }
    }
    val two= async<Int> {
        printMsg("second child throw an exception")
        throw ArithmeticException()
    }
    one.await()+two.await()
}

image.png try finally就是前面讲过的,协程抛异常没有捕捉,就奔溃了,防止奔溃,可以在协程体上try catch。

fun main() = runBlocking {
    try {  //#1
        launch(Dispatchers.Default) {
            try { //#2
                val time = measureTimeMillis {
                    printMsg("${concurrentSum()}")
                }
                printMsg("${time}")
            } catch (e: Exception) {
                printMsg("#2  $e")
            }
        }
        printMsg("Done")
    } catch (e: Exception) {
        printMsg("#1  $e")
    }
}

image.png try catch 这里有两处,#1 和 #2 ,这里能catch这个异常只能是#2,注意,仅try catch最外层#1是捕捉不了这个异常的,app会奔溃。