不知不觉来到了第28天,最後我们来做个复习吧。
用来解决非同步程序执行的问题,在以前面对非同步的程序时,我们可能需要建立一个新的执行绪,在这个执行绪上执行完耗时任务之後,接者再把结果由此执行绪传回至原本的执行绪。
因为非同步程序的特性,我们需要使用 Callback 在耗时任务结束後把结果回传回来,但是如果有好几个 Callback 嵌套,也就是一个结果会传至另一个函式作为参数,这样就会产生所谓的 Callback hell(回调地狱),增加了维护的难度。
Coroutine 的目的就是要让非同步的程序执行就像是同步的程序执行一样,也就是说不需要 Callback 来接收不同执行绪的结果。
cancel()
只要呼叫 cancel()
该 Coroutine 的任务就会被取消。我们将耗时任务写在 suspend 函式中,当程序执行到 suspend 函式时,将会暂停该 coroutine ,并且切换自动切换至其他的 coroutine,等到 suspend 函式完成它的任务时,就会在原本暂停的点继续开始下面的任务。
suspend 函式只能在 coroutine scope 或是另外的 suspend 函式内呼叫。
要执行 suspend 函式,必须要在 CoroutineScope 中执行,而建立 Scope 的方式有两种,一种是 launch
,另一种则是 async
。其中 launch 是没有回传值的(Side-effect),而 async 是有回传值的。launch 回传的是 Job,而 async 回传的是 Deferred,其中 Deferred 也是继承 Job 的类别。如前面所说,我们可以直接呼叫 Job 的 cancel() 取消 coroutine 的任务。
建立执行绪是需要较多资源,如果每次需要耗时任务都建立一个执行序,那麽是不太切实际的,所以Coroutine 中,使用者是不能直接使用执行绪的,这边提供了几种不同的 Dispatchers,使用者根据需求选择不同的 Dispatchers,Coroutine 就会选择适当的执行绪/执行绪池。这些执行绪/执行绪池是不会被关闭的,所以任务可以很快速的切换至不同的执行绪中。另外,如果要在 Coroutine 中切换不同的调度器,我们可以使用 withContext
。
我们可以在一个 Coroutine Scope 中执行多个 Job,它们彼此之间的关系是父-子的关系,也就是说,当 Coroutine Scope 里面的所有 Job 结束之後,外层(父层)的 Coroutine 才能够结束。如果其中一个子 Coroutine 发生错误被取消,那麽後面还没有完成的任务也会一并被取消。另外,如果父 coroutine 被取消,那麽所有在里面的子 Coroutine 也会一并被取消。这样子的好处就是不会有父类别已经被取消,但是子 coroutine 却还在执行,这样子就有可能会发生 memory leak 的情况。
如果在一个 CoroutineScope 中,其中一个子 coroutine 被取消,如果我们使用的是 Job(),那麽後面的所有 job 都会被取消。但是如果使用的是 SupervisorJob(),当其中一个 Job 发生错误被取消时,後面的任务还是会继续执行直到完成。
在 coroutine 中,我们一样可以使用 try-catch 来将可能会发生例外(Exception) 的程序码包起来,当发生例外的时候,就会被 try-catch 给拦截下来。如果我们希望能够在 CoroutineScope 的范围内能够捕捉例外呢?我们可以使用 CoroutineExceptionHandler。不过 async
的例外会在呼叫 await()
时才会发生,所以如果要捕捉 async 的例外,则还是必须要在 await() 上使用 try-catch 。
呼,前十四天的内容就在这篇做个复习了,如果有什麽遗漏,或是有错误,麻烦请跟我说,下一篇将复习後面的部分: Channel、Flow、SharedFlow、StateFlow。
<<: DAY 21 Big Data 5Vs – Variety(速度) Kinesis (1)
以工程师职涯发展上有不少的论点跟方向,但大多来说似乎一定要走上管理职在履历上才有所突破。 在写这篇文...
おっはー(U 'ᴗ' U)⑅ 我是SONYKO 。 今天天气好好。连假我还真tm的都在家写文章呢 ...
JavaScript feature 随着越来越深入JavaScript,现在所考察和学习到的co...
After understanding the basic HTML DOM Event conce...
终於度过前面枯燥乏味的内容了...(但它们都很重要,也与今天的主题有关) 今天要来进入重点项目 我们...