介绍 Swift 异步算法

作为 Swift 向安全、简洁和高性能异步编程迈进的一部分,我们很高兴推出一个新的 AsyncSequence 算法包。它被称为 Swift Async Algorithms,现已在 GitHub 上提供。

这个包有三个主要目标

动机

AsyncAlgorithms 是一个用于处理随时间变化的值的算法包。这包括主要关于时间的算法,例如 debouncethrottle,也包括关于顺序的算法,例如 combineLatestmerge。处理多个输入的操作(例如 zipSequence 上所做的操作)实现起来可能非常复杂,具有微妙的行为和许多需要考虑的边缘情况。一个共享包可以正确处理这些细节,并进行广泛的测试和文档记录,从而使所有 Swift 应用程序受益。

AsyncAlgorithms 的基础已经包含在 Swift 5.5 的 AsyncSequence 中。Swift 5.5 还带来了使用自然的 for/in 循环和 await 来处理 AsyncSequenceSequence 等效 API(如 mapfilter)中的值的能力。结构化并发允许我们编写代码,其中中间状态只是一个局部变量,try 可以直接用于 throw 的函数,并且通常将异步代码的逻辑视为类似于同步代码的逻辑。

我们相信开源包将为这些 API 提供一个很好的家。包为开发人员提供了跨平台和操作系统版本部署的灵活性。开发和 API 设计将在 GitHubSwift 论坛上进行。

简要浏览

该包包括熟悉的算法的 AsyncSequence 版本,例如


让我们从看看 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
}

其他算法(如 combineLatestmerge)也组合了多个 AsyncSequence 的输出。每个算法都对输出的类型和时序提供了不同类型的控制。


SequenceAsyncSequence 之间的一个根本区别是引入了时间变量。在该包的基础上,标准化 Clock 和 Duration 的提案添加了诸如 debouncethrottle 之类的算法。它们为常见的操作(例如丢弃到达速度过快的值)提供了简单、开箱即用的解决方案

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 函数对于将同步 SequenceAsyncSequence 结合使用非常有用。在这里,我们将其与 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 基于 PublisherSubscriber 接口,运算符在它们之间建立连接。它的设计重点是提供一种声明式方式来指定这些运算符的链,从而在数据从一端移动到另一端时对其进行转换。这需要以不同的方式考虑中间状态。有时,这会导致调用站点比人们预期的更复杂——尤其是在处理单个值、错误或需要共享的数据时。async/await结构化并发为我们提供了一种表达此类逻辑的新方法。我们现在可以将异步代码分成更小的部分,并从上到下读取,而不是作为一系列链式转换。

我们对 async/awaitAsyncSequence 为该语言带来的可能性感到兴奋。我们相信这个包将是探索该领域更高级别 API 的未来发展和演进的绝佳场所。

下一步是什么

今天,我们发布了 Swift Async Algorithms 包的原型版本。我们的目的是通过一个可用的实现来启动该项目,然后在 Swift 论坛上继续进行详细的设计讨论。我们欢迎社区参与

我们正在使用 GitHub Issues 来跟踪错误、功能请求和入门任务。

参考资料

文档

配套软件包