返回顶部
首页 > 资讯 > 移动开发 >KotlinSuspend挂起函数的使用详解
  • 485
分享到

KotlinSuspend挂起函数的使用详解

KotlinSuspend挂起函数KotlinSuspendKotlin挂起函数 2023-02-17 15:02:39 485人浏览 泡泡鱼
摘要

目录总结本质何时使用消除回调一些例子总结 挂起(suspend)函数是所有协程的核心。 挂起函数可以执行长时间运行的操作并等待它完成而不会阻塞主线程。 挂起函数的语法与常规函数的语法

总结

挂起(suspend)函数是所有协程的核心。 挂起函数可以执行长时间运行的操作并等待它完成而不会阻塞主线程

挂起函数的语法与常规函数的语法类似,不同之处在于添加了suspend关键字。 它可以接受一个参数并有一个返回类型。 但是,挂起函数只能由另一个挂起函数或在协程内调用。

suspend fun backgroundTask(param: Int): Int {
     // long running operation
}

在背后,编译器将挂起函数转换为另一个没有挂起关键字的函数,该函数接受一个类型为 Continuation<T> 的附加参数。 例如,上面的函数将由编译器转换为:

fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
   // long running operation
}

本质

  • 挂起函数只能在协程或者其他挂起函数中调用。
  • 挂起的对象是协程:launch ,async 或者其他函数创建的协程,在执行到某一个 suspend 函数的时候,这个协程会被挂起,即,从正在执行它的线程上脱离。就是说,当前线程跳过这个挂起函数,继续往下运行,但另一方面,线程的代码在到达 suspend 函数的时候被掐断,接下来协程会从这个 suspend 函数开始继续往下执行,不过是在指定的线程,执行完后,返回到之前挂起它的线程;
  • 简单来讲,在 Kotlin 中所谓的挂起,就是一个稍后会被自动切回来的线程调度操作;
  • 挂起函数的特点是使用同步的方式完成异步任务。
  • withContext 的作用就是指定切换的线程,比如:suspend fun suspendingGetImage(id: String) = withContext(Dispatchers.IO)

何时使用

如果你的某个函数比较耗时,也就是要等的操作,那就把它写成 suspend 函数。这就是原则。

耗时操作一般分为两类:I/O 操作和 CPU 计算工作。比如文件的读写、网络交互、图片的模糊处理,都是耗时的,通通可以把它们写进 suspend 函数里。

另外这个「耗时」还有一种特殊情况,就是这件事本身做起来并不慢,但它需要等待,比如 5 秒钟之后再做这个操作。这种也是 suspend 函数的应用场景。

消除回调

假设 postItem 由三个有依赖关系的异步子任务组成: requestTokencreatePostprocessPost,这三个函数都是基于回调的 api

// 三个基于回调的 API
fun requestToken(block: (String) -> Unit)
fun createPost(
  token: String,
  item: Item,
  block: (Post) -> Unit)
)
fun processPost(post: Post)
fun postItem(item: Item) {
  requestToken { token ->
    createPost(token, item) { post ->
      processPost(post)
    }
  }
}

可以看到基于回调的 API 很容易造成大量缩进。如果代码中再加上一些条件、循环的逻辑,那么代码可读性会大大降低。Kotlin 的 suspend 关键字可以帮助我们消除回调,用同步的写法写异步:

suspend fun requestToken(): String
suspend fun createPost(token: String, item: Item): Post
suspend fun processPost(post)
suspend fun postItem(item: Item) {
  val token = ? requestToken()
  val post = ? createPost(token, item)
  ? processPost(post)
}

由于 createPost 这些方法实际上是耗时的 IO 异步操作,需要等到拿到返回值才能执行后面的逻辑,但我们又不希望阻塞当前线程(通常是主线程),因此最终必须实现某种消息传递的机制,让后台线程做完耗时操作以后把结果传给主线程。

一些例子

一个基本的使用方式:

suspend fun getUserInfo(): String {
    withContext(Dispatchers.IO) {
        delay(1000L)
    }
    return "BoyCoder"
}

在 Room 里面会经常用到:

@Dao
interface ReGISterDatabaseDao {
    @Insert
    suspend fun insert(register: RegisterEntity)
    //@Delete
    //suspend  fun deleteSubscriber(register: RegisterEntity):Int
    @Query("SELECT * FROM Register_users_table ORDER BY userId DESC")
    fun getAllUsers(): LiveData<List<RegisterEntity>>
    @Query("DELETE FROM Register_users_table")
    suspend fun deleteAll(): Int
    @Query("SELECT * FROM Register_users_table WHERE user_name LIKE :userName")
    suspend fun getUsername(userName: String): RegisterEntity?
}

最后这个例子可以直接在 Kotlin Playground 上跑。

import kotlinx.coroutines.*
import java.util.*
import java.time.LocalDate
import java.time.fORMat.DateTimeFormatter
import java.time.Period
import java.text.SimpleDateFormat
import java.lang.Thread
var dateTimeNow = ""
@OptIn(DelicateCoroutinesApi::class)
fun main() = runBlocking{
    dateTimeNow = dateAsString(Calendar.getInstance().time.time)
    println("code start: ${dateTimeNow}")
    launch { 
        dateTimeNow = dateAsString(Calendar.getInstance().time.time)
    	println("1 code start: ${dateTimeNow}")
        delay(2000L)
        dateTimeNow = dateAsString(Calendar.getInstance().time.time)
        println("2 Task from runBlocking: ${dateTimeNow}")
    }
    coroutineScope { // Creates a new coroutine scope
        dateTimeNow = dateAsString(Calendar.getInstance().time.time)
        println("3 coroutineScope created: ${dateTimeNow}")
        val job = launch {
            dateTimeNow = dateAsString(Calendar.getInstance().time.time)
            println("4 coroutineScope job starts: ${dateTimeNow}")
            val one = doSomethingUsefulOne()
        	  val two = doSomethingUsefulTwo()
            dateTimeNow = dateAsString(Calendar.getInstance().time.time)
            println("5 coroutineScope job ends: ${dateTimeNow}")
        }
        val job2 = launch {
            dateTimeNow = dateAsString(Calendar.getInstance().time.time)
            println("11 coroutineScope job2 starts: ${dateTimeNow}")
        }
        delay(1000L)
        dateTimeNow = dateAsString(Calendar.getInstance().time.time)
        println("6 Task from first coroutine scope: ${dateTimeNow}") // Printed before initial launch
        //job.cancel() // This cancels nested launch's execution
    }
    dateTimeNow = dateAsString(Calendar.getInstance().time.time)
    println("code end: ${dateTimeNow}")
}
fun dateAsString(
    dateInMillis: Long,
    format: String = "yyyyMMdd HH:mm:ss",
    locale: Locale = Locale.getDefault()
): String {
    val date = Date(dateInMillis)
    val formatter = SimpleDateFormat(format, locale)
    return formatter.format(date)
}
suspend fun doSomethingUsefulOne(): Int {
    dateTimeNow = dateAsString(Calendar.getInstance().time.time)
    println("7 第一个挂起函数开始: ${dateTimeNow}")
    delay(1000L) // 假设我们在这里做了某些有用的工作
    dateTimeNow = dateAsString(Calendar.getInstance().time.time)
    println("8 第一个挂起函数结束: ${dateTimeNow}")
    return 1
}
suspend fun doSomethingUsefulTwo(): Int {
    dateTimeNow = dateAsString(Calendar.getInstance().time.time)
    println("9 第二个挂起函数开始: ${dateTimeNow}")
    delay(2000L) // 假设我们在这里也做了某些有用的工作
    dateTimeNow = dateAsString(Calendar.getInstance().time.time)
    println("10 第二个挂起函数结束: ${dateTimeNow}")
    coroutineScope {
        val job = launch {
            doSomethingUsefulThree()
            doSomethingUsefulFour()
        }
    }
    return 2
}
suspend fun doSomethingUsefulThree(): Int {
    dateTimeNow = dateAsString(Calendar.getInstance().time.time)
    println("9 第三个挂起函数开始: ${dateTimeNow}")
    delay(3000L) // 假设我们在这里也做了某些有用的工作
    dateTimeNow = dateAsString(Calendar.getInstance().time.time)
    println("10 第三个挂起函数结束: ${dateTimeNow}")
    return 3
}
suspend fun doSomethingUsefulFour(): Int {
    dateTimeNow = dateAsString(Calendar.getInstance().time.time)
    println("9 第四个挂起函数开始: ${dateTimeNow}")
    delay(3000L) // 假设我们在这里也做了某些有用的工作
    dateTimeNow = dateAsString(Calendar.getInstance().time.time)
    println("10 第四个挂起函数结束: ${dateTimeNow}")
    return 4
}

打印的结果如下:

code start: 20221009 03:15:55
3 coroutineScope created: 20221009 03:15:55
1 code start: 20221009 03:15:55
4 coroutineScope job starts: 20221009 03:15:55
11 coroutineScope job2 starts: 20221009 03:15:55
7 第一个挂起函数开始: 20221009 03:15:55
6 Task from first coroutine scope: 20221009 03:15:56
8 第一个挂起函数结束: 20221009 03:15:56
9 第二个挂起函数开始: 20221009 03:15:56
2 Task from runBlocking: 20221009 03:15:57
10 第二个挂起函数结束: 20221009 03:15:58
9 第三个挂起函数开始: 20221009 03:15:58
10 第三个挂起函数结束: 20221009 03:16:01
9 第四个挂起函数开始: 20221009 03:16:01
10 第四个挂起函数结束: 20221009 03:16:04
5 coroutineScope job ends: 20221009 03:16:04
code end: 20221009 03:16:04

有几点需要说明:

  • launch 是 CoroutineScope 的一个扩展函数,该方法在不阻塞当前线程的情况下启动新的协程,launch 里面的代码虽然有挂起函数,但还是会按顺序运行(注意,这里的挂起函数并没有用withContext选择去指定切换的线程);
  • coroutineScope 本身就是一个挂起函数,会挂起当前的协程。coroutineScope 里面的代码除了 launch,其他按照顺序运行,而 coroutineScope 里面可以 launch 多个 job,这多个 job 是并行的;
  • suspend 挂起函数里面的挂起函数是(默认)串行的(即,用同步的方式实现异步)。

到此这篇关于Kotlin Suspend挂起函数的使用详解的文章就介绍到这了,更多相关Kotlin Suspend挂起函数内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: KotlinSuspend挂起函数的使用详解

本文链接: https://lsjlt.com/news/196602.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • KotlinSuspend挂起函数的使用详解
    目录总结本质何时使用消除回调一些例子总结 挂起(suspend)函数是所有协程的核心。 挂起函数可以执行长时间运行的操作并等待它完成而不会阻塞主线程。 挂起函数的语法与常规函数的语法...
    99+
    2023-02-17
    Kotlin Suspend挂起函数 Kotlin Suspend Kotlin挂起函数
  • Kotlin挂起函数的详细介绍
    Kotlin 协程的优势: 解决回调地狱的问题。以同步的方式完成异步任务。 示例: fun main() { runBlocking { val a = ge...
    99+
    2024-04-02
  • Kotlin挂起函数CPS转换原理解析
    目录正文1.什么是CPS转换2.CPS的过程是怎么让参数改变的3.CPS的过程是怎么让返回值改变的4.挂起函数的反编译5.非挂起函数的分析正文 普通函数加上suspend之后就成为了...
    99+
    2022-12-08
    Kotlin 挂起函数CPS转换 Kotlin 挂起函数
  • 详解C# 线程的挂起与唤醒
    目录一,AutoResetEvent类二,ManualResetEvent     如果说C#和C++有什么不同,博主不得不说,对于异步的支持程度是C#...
    99+
    2024-04-02
  • JavaScript函数的使用详解
    目录1. 声明函数2. 调用函数3. 函数的参数4. 函数的返回值5. arguments的使用6. 函数可以调用另外一个函数7. 函数的两种声明方式总结1. 声明函数2. 调用函数...
    99+
    2024-04-02
  • Python的函数使用详解
    目录前言1 跳出循环-break2 python函数2.1 内置函数2.2 自定义函数2.3 main函数前言 在两种python循环语句的使用中,不仅仅是循环条件达到才能跳出循环体...
    99+
    2024-04-02
  • tensor.squeeze函数和tensor.unsqueeze函数的使用详解
    tensor.squeeze() 和 tensor.unsqueeze() 是 PyTorch 中用于改变 tensor 形状的两个函数,它们的作用如下: tensor.squeez...
    99+
    2023-03-09
    tensor.squeeze函数 tensor.unsqueeze函数
  • Linux lseek函数的使用详解
    注:如果文章内容有误,请留言指出,谢谢合作。 名字 Name : lseek - reposition read/write file offset lseek函数的作用是用来重新定位文件读写的位移。 头文件以及函数声...
    99+
    2022-06-04
    Linux lseek函数 Linux lseek
  • MySQL函数Locate的使用详解
    Locate函数主要的作用是判断一个字符串是否包含另一个字符串,如 Locate(str,sub) > 0,表示sub字符串包含str字符串;Locate(str,sub) = 0,表示sub字符串不包含str字符...
    99+
    2022-08-15
    MySQLLocate函数 MySQLLocate
  • Vue h函数的使用详解
    目录一、认识二、使用1、h() 参数2、简单的使用3、实现一个计数器案例4、函数组件和插槽的使用三、jsx的使用1、jsx的认识2、下载Babel插件支持vue(现在貌似脚手架直接支...
    99+
    2024-04-02
  • Kotlin协程与挂起函数及suspend关键字深入理解
    目录1.挂起函数2.深入理解suspend3.协程与挂起函数4.挂起函数是Kotlin协程的最大优势5.总结1.挂起函数 挂起函数在Kotlin协程中是一个比较重要的知识点,协程的非...
    99+
    2022-12-08
    Kotlin 协程挂起函数suspend Kotlin 挂起函数
  • python3中apply函数和lambda函数的使用详解
    目录lambda函数lambda是什么lambda用法详解lambda + maplambda + filterlambda + reduce避免过度使用lamb...
    99+
    2024-04-02
  • C++ push_back()函数使用详解
    最近在学习Opencv,用C++写程序,做了一个虚拟画笔的项目,即通过摄像头采集视频图像信息,识别视频中的画笔,并画笔在空中的划痕显示在视频图像上。在进行到划痕显示的,由于视频是实时...
    99+
    2024-04-02
  • python 使用enumerate()函数详解
    目录一、enumerate() 函数简介二、使用enumerate()函数(1)使用for循环(2)使用enumerate()一、enumerate() 函数简介 enumerate...
    99+
    2024-04-02
  • MFC MoveWindow();函数使用详解
    MFC的MoveWindow()函数用于移动和调整窗口的位置和大小。它可以用于MFC中的CWnd类的对象,包括对话框、窗口和控件等。...
    99+
    2023-09-02
    MFC
  • python 使用enumerate()函数详解
    一、enumerate() 函数简介 enumerate()是python的内置函数,将一个可遍历iterable数据对象(如list列表、tuple元组或str字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在for循环当中。 ...
    99+
    2023-09-08
    python 开发语言
  • VUErender函数使用和详解
    目录前言render的作用render函数讲解render和template的区别render举例总结 前言 在平时编程时,大部分是通过template来创建html。但是在一些特殊...
    99+
    2024-04-02
  • Java中Pattern.compile函数的使用详解
    目录Java Pattern.compile函数的使用Pattern类的compile()方法还有另一个版本:flag来自以下Pattern类中的常量:我们可以通过“或”( | )操...
    99+
    2024-04-02
  • 详解Python中enumerate函数的使用
    Python 的 enumerate() 函数就像是一个神秘的黑箱,你无法简单地用一句话来概括这个函数的作用与用法。 enumerate() 函数属于非常有用的高级用法,而对于这一点...
    99+
    2024-04-02
  • 详解Python之find函数的使用
    目录          一、find函数的官方定义          二、find函数的详细函数使用解释 一、find函数的官方定义 首先,Python的find函数多用在字符串的处理上,也是Python计算机二级的小考点。 定义:P...
    99+
    2023-10-12
    python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作