Hello, 异步函数
1 | func hello() async -> String { |
Kotlin:
1 | suspend fun hello(): String { |
结构化并发
1 | let task = Task { |
Kotlin:
1 | val scope = CoroutineScope(EmptyCoroutineContext) |
Task
async标记的函数只能在async标记的函数或闭包中调用, Task的初始化器提供了async闭包环境:
1 | public init(priority: TaskPriority? = nil, operation: @escaping () async -> Success) |
可选使用TaskPriority参数:
1 | Task(priority: .high) { |
取值:
1 | high, medium, low, |
后面三种等级类似于DispatchQos
.
withUnsafeCurrentTask
如果要获取当前task, 可以使用withUnsafeCurrentTask
:
1 | withUnsafeCurrentTask { task in |
此函数非async, 如果当前环境不存在task, task值为nil.
如果仅想检查任务优先级和任务是否取消, 可以直接使用Task.currentPriority
和Task.isCancelled
.
Task嵌套
对于Task嵌套的情况:
1 | Task { |
子Task会继承当前Task的环境(但仍然是独立的Task, 非结构化并发), 如果不想继承可以使用Task.detached(priority:operation:)
:
1 | Task { |
CancelBag
task离开作用域时不会被取消, 如果需要可以仿照DisposeBag实现CancelBag(非线程安全):
1 | public final class CancelBag { |
使用:
1 | let bag = CancelBag() |
要注意循环引用. 如果不想在task闭包中处理循环引用, 可以使用手动的CancelBag:
1 | public final class CancelBag { |
并在适当时机调用bag.cancel()
.
async let
1 | async let hello = hello() |
Kotlin:
1 | val hello = async { hello() } |
同时执行多个async函数.
比如发送网络请求时, 如果第三个请求的参数依赖前两个请求的结果:
1 | async let x = reqX() |
可以让x, y请求同时发出, 都得到响应后, 再发出z请求
for await
1 | func number() async -> Int { |
Kotlin:
1 | suspend fun number() = Random.nextInt(0..100) |
for await
用于迭代实现了AsyncSequence
协议的类型, 如TaskGroup
和下文将要提到的AsyncStream
.
withTaskGroup(of:)
效果类似于corountineScope
, 可以添加一系列子任务并等待这些任务完成.
如果子Task会调用throws, 可以使用withThrowingTaskGroup
:
1 | try await withThrowingTaskGroup(of: Int.self) { group in |
await all
1 | await withTaskGroup(of: Int.self) { group in |
1 | (1..100) |
收集子任务结果.
AsyncStream
1 | let stream = AsyncStream<Int> { continuation in |
1 | let stream = AsyncStream<Int> { |
Kotlin:
1 | flow { |
1 | flow { |
这里AsyncStream和flow对比比较直观, 但其行为更像ReceiveChannel, 因为flow是冷的.
Actor
1 | actor Counter { |
1 | let couter = Counter() |
Kotlin:
1 | sealed interface CounterMsg |
1 | val counter = counterActor() |
Swift在语言层面支持了actor, 因此使用actor要比Kotlin方便许多.
从外部调用actor中的函数、可变属性和计算属性需要使用await. 如果确定不存在数据竞争, 可以用nonisolated
修饰:
1 | nonisolated func foo() -> String { |
这样就可以像调用普通函数一样从外部调用.
GlobalActor
用@globalActor创建一个修饰器, 用此修饰器修饰, 可以让函数/属性/class共同使用一个actor环境:
1 |
|
MainActor
MainActor是运行于主线程的GlobalActor:
1 | final public actor MainActor : GlobalActor |
可以把更新UI的函数用@MainActor修饰:
1 |
|
以保证此函数一定在主线程执行.
Sendable
Sendable是一个mark协议, 用于标记类型为可安全在actor间移动的, 类似于Rust的Send
trait.
1 | public protocol Sendable { |
以下类型是Sendable的:
基本类型
actor类型
由Sendable类型构成的struct
满足以下条件的@Sendable修饰的函数或闭包:
捕获变量是Sendable类型
捕获变量不可变
满足以下条件的显式实现Sendable的class:
final修饰, 即不可继承
存储属性是Sendable类型
存储属性不可变
无父类或父类是NSObject
如果确定不符合以上条件的class可以安全Send, 可以实现@unchecked Sendable以跳过编译检查:
1 | class Foo: @unchecked Sendable { |
从Callback迁移
1 | func request(_ completion: (String) -> Void) { |
Kotlin:
1 | fun request(callback: (String) -> Unit) { |
如果闭包内调用了throws, 可以使用withCheckedThrowingContinuation(function:_:)
如果可以确保调用且仅调用一次resume
, 可以使用unsafe版本:
withUnsafeContinuation(_:)
withUnsafeThrowingContinuation(_:)