Kotlin suspend 关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的协程提供了一种强大的方式来编写异步代码。suspend
关键字标记可以暂停执行而不阻塞线程的函数。本教程通过实际例子深入探讨了挂起函数。
基本定义
suspend
关键字将函数标记为挂起函数。此类函数可以暂停执行,稍后恢复,而不会阻塞线程。它们只能从协程或其他挂起函数中调用。挂起函数是 Kotlin 协程系统的核心。
基本挂起函数
一个简单的挂起函数演示了基本语法。该函数可以执行长时间运行的操作,而不会阻塞调用线程。它使用 Kotlin 协程库中的 delay
函数。
package com.zetcode import kotlinx.coroutines.* suspend fun greet() { delay(1000) println("Hello from suspend function!") } fun main() = runBlocking { greet() println("Done") }
此示例显示了一个简单的挂起函数 greet
。runBlocking
协程构建器创建协程上下文。该函数使用 delay
暂停 1 秒,然后打印一条消息。
带参数的挂起函数
挂起函数可以像常规函数一样接受参数。它们也可以返回值。这使得它们对各种异步操作具有灵活性。该示例显示了一个处理输入的挂起函数。
package com.zetcode import kotlinx.coroutines.* suspend fun processData(data: String): String { delay(500) return "Processed: $data" } fun main() = runBlocking { val result = processData("Kotlin") println(result) // Output: Processed: Kotlin }
processData
函数接受一个 String 参数,并在延迟后返回一个处理后的版本。runBlocking
协程构建器允许从常规代码调用此挂起函数。
顺序挂起调用
挂起函数可以按顺序调用其他挂起函数。执行在每个挂起点暂停。这创建了可读的异步代码,看起来像同步代码。
package com.zetcode import kotlinx.coroutines.* suspend fun fetchUser(): String { delay(1000) return "User123" } suspend fun fetchPosts(user: String): List<String> { delay(1000) return listOf("Post1", "Post2") } fun main() = runBlocking { val user = fetchUser() val posts = fetchPosts(user) println("$user has posts: $posts") }
此示例显示了按顺序调用的两个挂起函数。首先运行 fetchUser
,然后 fetchPosts
使用其结果。总执行时间约为 2 秒,但代码仍然干净。
使用 async 进行并行执行
挂起函数可以使用 async
并行运行。这对于可以并发执行的独立操作很有用。await
函数暂停,直到结果准备就绪。
package com.zetcode import kotlinx.coroutines.* suspend fun fetchProfile(): String { delay(800) return "Profile Data" } suspend fun fetchNotifications(): List<String> { delay(600) return listOf("Notification1", "Notification2") } fun main() = runBlocking { val profileDeferred = async { fetchProfile() } val notificationsDeferred = async { fetchNotifications() } val profile = profileDeferred.await() val notifications = notificationsDeferred.await() println("$profile with $notifications") }
在这里,两个挂起函数使用 async
并行运行。总执行时间约为 800 毫秒(最长的操作),而不是 1400 毫秒。await
调用暂停,直到两个操作完成。
带有 try-catch 的挂起函数
挂起函数可以使用标准 Kotlin 错误处理。异常的工作方式与常规函数相同。这使得协程中的错误处理变得熟悉而直接。
package com.zetcode import kotlinx.coroutines.* suspend fun riskyOperation(): String { delay(500) if (Math.random() > 0.5) { throw RuntimeException("Operation failed!") } return "Success" } fun main() = runBlocking { try { val result = riskyOperation() println(result) } catch (e: Exception) { println("Caught: ${e.message}") } }
riskyOperation
函数可能会抛出异常。我们像使用常规函数一样,将调用包装在 try-catch 块中。异常处理在挂起函数中的工作方式相同。
挂起 Lambda 表达式
Lambda 表达式也可以标记为挂起。这允许编写可以传递的异步代码块。该示例显示了与 withContext
一起使用的挂起 lambda。
package com.zetcode import kotlinx.coroutines.* suspend fun performOperation(block: suspend () -> Unit) { println("Operation starting") block() println("Operation complete") } fun main() = runBlocking { performOperation { delay(1000) println("Middle of operation") } }
performOperation
函数接受一个挂起 lambda。lambda 可以包含挂起调用,例如 delay
。这种模式在基于协程的库中很常见。
挂起函数中的协程上下文
挂起函数可以访问协程上下文。它们可以使用 coroutineContext
检查或修改它。这对于结构化并发和上下文传播很有用。
package com.zetcode import kotlinx.coroutines.* suspend fun printContextInfo() { println("Running in ${coroutineContext[CoroutineName]?.name}") println("Dispatcher: ${coroutineContext[CoroutineDispatcher]}") } fun main() = runBlocking(CoroutineName("MainCoroutine")) { printContextInfo() }
printContextInfo
函数访问协程上下文。它打印协程名称和调度器。上下文从父协程传递给挂起函数。
挂起函数的最佳实践
- 保持专注:每个挂起函数都应该做好一件事,就像常规函数一样。
- 适当地命名它们:使用表明它们可能暂停执行的名称。
- 处理取消:确保长时间运行的操作检查取消。
- 记录挂起点:在文档中注明函数可能暂停的位置。
- 考虑上下文:注意你的函数将在其中运行的协程上下文。
来源
本教程深入介绍了 Kotlin 的 suspend
关键字,展示了如何编写和使用挂起函数。我们探讨了各种场景,包括顺序和并行执行、错误处理和上下文感知。正确使用挂起函数可以使异步代码更干净、更易于维护。
作者
列出 所有 Kotlin 教程。