作为 Swift 向安全、简单和高性能异步编程迈进的一部分,Swift 核心团队引入了一个新的算法包:AsyncSequence。这个算法包被称为 Swift Async Algorithms,现在已开源并可以在 GitHub 上获取 https://github.com/apple/swift-async-algorithms。
这个包有三个主要目标:
- 与 async/await 的集成
- 为基于时间的算法提供一个归宿
- 跨平台和开源
目标
AsyncAlgorithms 是一个算法包,用于处理随时间变化的值。这包括主要与时间有关的算法,例如 debounce 和 throttle,还包括有关顺序的算法,例如 combineLatest 和 merge。使用多个输入的操作(如 zip 对 Sequence 所做的操作)实现起来可能非常复杂,需要考虑微妙的行为和许多边缘情况。共享包可以通过大量测试和文档来正确处理这些细节,以造福所有 Swift 应用程序。
AsyncAlgorithms 的基础能力已经包含在 Swift 5.5 的 AsyncSequence 中。Swift 5.5 还带来了使用自然 for/in 循环和 await 来处理 AsyncSequence 和序列等效 API(如 map 和 filter)中的值的能力。结构化并发允许我们编写中间状态只是一个局部变量的代码,try 可以直接用于抛出异常的函数,并且异步代码的逻辑与同步代码类似。
我们相信开源包将为这些 API 提供一个很好的归宿。软件包使开发人员可以灵活地跨平台和操作系统版本进行部署。开发和 API 设计将在 GitHub 和 Swift 论坛上进行。
简要概览
该软件包包括常用算法的 AsyncSequence 版本,例如:
- Zip
- CombineLatest
- Merge
- Chain
- Buffer
- Debounce
- Throttle
让我们从 zip 开始。与其对应的序列一样,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 的一个根本区别是引入了时间变量。基于 proposals to standardize Clock and Duration 这个 proposal https://github.com/apple/swift-evolution/blob/main/proposals/0329-clock-instant-duration.md ,该软件包添加了 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 函数对于将同步序列与 AsyncSequence 结合起来很有用。在这里,我们将它与 chain 函数一起使用来为文件的内容添加前导:
let preamble = [
"// This source file is part of the Swift.org 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 官方现在发布了 Swift 异步算法包的原型版本。目的是通过有效的实现来启动项目,然后在 Swift 论坛上进行详细的设计讨论。欢迎社区参与:
- 早期采用该软件包和对设计的反馈
- 包的实现
- 测试的实施
- 包未来的宣传和演变
官方也正在使用 GitHub 问题来跟踪错误、功能请求和启动任务。