介绍 Swift 异步算法
作为 Swift 向安全、简洁和高性能异步编程迈进的一部分,我们很高兴推出一个新的 AsyncSequence
算法包。它被称为 Swift Async Algorithms,现已在 GitHub 上提供。
这个包有三个主要目标
- 与
async/await
的一流集成 - 为基于时间的算法提供一个家
- 跨平台和开源
动机
AsyncAlgorithms 是一个用于处理随时间变化的值的算法包。这包括主要关于时间的算法,例如 debounce
和 throttle
,也包括关于顺序的算法,例如 combineLatest
和 merge
。处理多个输入的操作(例如 zip
在 Sequence
上所做的操作)实现起来可能非常复杂,具有微妙的行为和许多需要考虑的边缘情况。一个共享包可以正确处理这些细节,并进行广泛的测试和文档记录,从而使所有 Swift 应用程序受益。
AsyncAlgorithms 的基础已经包含在 Swift 5.5 的 AsyncSequence 中。Swift 5.5 还带来了使用自然的 for/in
循环和 await
来处理 AsyncSequence
和 Sequence
等效 API(如 map
和 filter
)中的值的能力。结构化并发允许我们编写代码,其中中间状态只是一个局部变量,try
可以直接用于 throw
的函数,并且通常将异步代码的逻辑视为类似于同步代码的逻辑。
我们相信开源包将为这些 API 提供一个很好的家。包为开发人员提供了跨平台和操作系统版本部署的灵活性。开发和 API 设计将在 GitHub 和 Swift 论坛上进行。
简要浏览
该包包括熟悉的算法的 AsyncSequence
版本,例如
- Zip
- CombineLatest
- Merge
- Chain
- Buffer
- Debounce
- Throttle
让我们从看看 zip
开始。与其 Sequence
对应项一样,zip
生成由来自两个不同 AsyncSequence
的值组成的元组
for await (number, letter) in zip(numbers, letters) {
print(number, letter)
}
AsyncSequence
支持 rethrows
,这意味着错误处理就像使用 try
一样简单——与任何其他 Swift 代码相同
do {
for try await (number, letter) in zip(numbers, lettersWithErrors) {
print(number, letter)
}
} catch {
// Handle error
}
其他算法(如 combineLatest
和 merge
)也组合了多个 AsyncSequence
的输出。每个算法都对输出的类型和时序提供了不同类型的控制。
Sequence
和 AsyncSequence
之间的一个根本区别是引入了时间变量。在该包的基础上,标准化 Clock 和 Duration 的提案添加了诸如 debounce
和 throttle
之类的算法。它们为常见的操作(例如丢弃到达速度过快的值)提供了简单、开箱即用的解决方案
for await value in input.debounce(for: .seconds(0.5)) {
// Handle input, at most once per 0.5 seconds.
}
等待有限异步序列中所有值的集合通常很有用。此包提供了使用单行代码执行此操作的初始化器
let result = await Array(input)
async
函数对于将同步 Sequence
与 AsyncSequence
结合使用非常有用。在这里,我们将其与 chain
函数一起使用,以将序言添加到文件的内容中
let preamble = [
"// This source file is part of the open source project"
"//"
""
].async
let lines = chain(preamble, URL(fileURLWithPath: "/tmp/Sample.swift").lines)
for try await line in lines {
print(line)
}
Combine
Apple 在 iOS 13 和 macOS 10.15 SDK 中引入了 Combine 框架。从那时起,我们有机会了解 Combine 在实际场景中的使用方式。借助 AsyncAlgorithms,我们正在应用这些经验教训,并拥抱 Swift 的新结构化并发功能。
Combine 的 API 基于 Publisher
和 Subscriber
接口,运算符在它们之间建立连接。它的设计重点是提供一种声明式方式来指定这些运算符的链,从而在数据从一端移动到另一端时对其进行转换。这需要以不同的方式考虑中间状态。有时,这会导致调用站点比人们预期的更复杂——尤其是在处理单个值、错误或需要共享的数据时。async/await
的 结构化并发为我们提供了一种表达此类逻辑的新方法。我们现在可以将异步代码分成更小的部分,并从上到下读取,而不是作为一系列链式转换。
我们对 async/await
和 AsyncSequence
为该语言带来的可能性感到兴奋。我们相信这个包将是探索该领域更高级别 API 的未来发展和演进的绝佳场所。
下一步是什么
今天,我们发布了 Swift Async Algorithms 包的原型版本。我们的目的是通过一个可用的实现来启动该项目,然后在 Swift 论坛上继续进行详细的设计讨论。我们欢迎社区参与
- 早期采用该软件包并就设计提供反馈
- 软件包的实现
- 测试的实现
- 软件包未来的提案和演进
我们正在使用 GitHub Issues 来跟踪错误、功能请求和入门任务。