大多数浏览器和
Developer App 均支持流媒体播放。
-
Swift Async 算法简介
探索 Apple 的最新开源 Swift 软件包:Swift Async 算法。我们将讨论此软件包的算法 (可搭配 AsyncSequence 使用),包括压缩、合并和限流。和我们一起学习如何使用这些算法来构建一流的信息收发 App。我们还将分享有关结合多个 AsyncSequence,以及长时间利用 Swift 时钟类型来处理相关值的最佳实践。为能更好地理解此讲座,我们建议您先观看 WWDC21 的“认识 AsyncSequence”。
资源
相关视频
WWDC22
WWDC21
-
下载
Philippe Hausler: 您好 我叫 Philippe Swift 拥有越来越多的 开源软件包 我很高兴向您介绍 最新添加的内容之一 Swift Async Algorithms 这个包和其他包一起 例如 Swift Collections 和 Swift Algorithms Swift Async Algorithms 包含一组算法 专门通过 AsyncSequence 按时序 处理数值 但在我们进入内容之前 让我们花点时间 回顾一下 AsyncSequence AsyncSequence 是一种协议 可以让您描述 通过异步过程产生的值 基本上 它与 Sequence 类似 但有两个关键区别 其迭代器的 next 函数是异步的 因为它可以使用 Swift 并发功能传递值 它还允许您使用 Swift 的抛出异常 以便于处理任何潜在的故障 并且您可以像 Sequence 一样 使用 for-await-in 语法 迭代它 总之 如果您知道 如何使用 Sequence 那么您就已经知道了 如何使用 AsyncSequence 现在 当引入 AsyncSequence 我们在异步版本中添加了 几乎所有可以用于 Sequence 的工具 例如 map、filter reduce 等算法 Swift Async Algorithms 通过合并更高级的算法 以及与时钟的互操作 向您提供一些真正强大的功能 让那些功能效果更上一层楼 这是一个开源的 AsyncSequence 算法包 可以增强 Swift 并发的功能 去年我们推出了 Swift Algorithms 包 为了演示这些算法的用途 我们制作了一个消息 App 这是一个很好的例子 您可以用这个包 做一些丰富和强大的东西 我们认为这是很好机会 通过迁移 App 使用 Swift 并发 为了强调一些异步算法的使用 我将带您了解我们使用的一些功能 以及它们是如何工作的 首先 我们有一系列 处理多输入 AsyncSequence 的算法 这些算法侧重于以不同的方式将 多个 AsyncSequence 组合在一起 但它们都有一个共同特点 它们接受多个 AsyncSequence 输入 并产生一个 AsyncSequence 输出
您可能已经非常熟悉 Zip Zip 算法接受多个 输入并对其进行迭代 以便通过每个基础内容 生成一个结果元组 Zip 的每个输入 都是构造 Zip 的基础内容 在标准库中 异步 Zip 算法的工作原理 与 Zip 算法一样 但它并发迭代每个基础内容 如果在迭代其中的 任何一个出现失败则重新抛出报错 现在 完成并发迭代 并支持重抛出错误会相当复杂 但是 Swift Async Algorithms 包 在我们的消息 App 中 为我们解决了所有这些问题 我们之前有很多代码 用来协同异步工作 比如生成视频录制预览和视频转码 以及生成各种尺寸的视频 用来实现高效存储和传输 通过使用 Zip 我们可以确保转码视频 在发送到服务器时得到预览 由于 Zip 是并发的 所以转码和预览 都不会对彼此的工作造成延迟 但更进一步想一下 Zip 本身对任何一边 都没有明显的偏向 所以可以先出视频 也可以出预览 不管它在哪一边 它都会 等待另一方发送完成一个完整的元组 我们可以等待视频和预览的资源配对 以便它们可以一起上传 因为 Zip 会同时等待 双方构造的元组的值 我们得出的结论是 对传入消息 作为 AsyncSequence 建模会很有意义 所以我们决定使用 AsyncStream 来处理这些消息 因为它保留了顺序并 将我们的回调转换为 消息的 AsyncSequence 实例 我们需要解决的功能要求之一是 我们希望支持多个帐户 所以每个帐户都会 构建一个用于传入消息的 AsyncStream 但在实现时 我们需要将它们作为一个单一的 AsyncSequence 一起进行处理 这意味着我们需要一种算法 来将这些 AsyncSequences 合并在一起 多亏 Swift Async Algorithms 包 有一个算法可以做到这一点 它的名字为 “Merge” 在并发迭代多个 AsyncSequences 方面 它的工作方式与 Zip 类似 但它不创建成对的元组 它要求共享相同的元素类型 并将基础 AsyncSequences 合并为 这些元素的一个单独的 AsyncSequence Merge 的工作方式是在迭代时 采用任何一边产生的第一个元素 它不断迭代 直到没有更多的值可以产生 具体就是当所有基本 AsyncSequence 从其迭代器返回 nil 时 如果有任何基础错误产生 则会取消其他迭代器的工作 这让我们可以获取 多个消息的 AsyncSequence 并合并它们 这些组合算法 在产生值时同时进行工作 但有时 与时间本身实际互动是有用的 Swift Async Algorithms 包 引入了一系列算法 通过利用 Swift 中新的 Clock API 来处理时间 时间本身可以是 一个非常复杂的主题 Swift (5.7) 中新增了 一组 API 以确保其安全性和一致性 Clock (时钟) 、Instant (即时) 和 Duration (持续时间)
Clock (时钟) 协议定义了两个基元 一种在给定时长后唤醒的方式 和一种产生现在概念的方式 它含有几个内置时钟 其中比较常用的两个是 ContinuousClock 和 SuspendingClock 您可以使用 ContinuousClock 像秒表一样测量时间 无论被测事物的状态如何 时间都在那里进行 另一方面 SuspendingClock 正如它的名字一样 当机器进入睡眠状态时它会挂起 我们在 App 中 使用了新的 Clock API 从现有的回调事件迁移到时钟睡眠功能 用来处理截止日期后解除警报 我们能够通过添加 持续时间值来创建截止日期 这个值需要特别表明 我们想要的延迟秒数 Clock (时钟) 也有一些方便的方法 来测量工作执行的持续时间 这里我们有之前 提到的两个常见的时钟 SuspendingClock 和 ContinuousClock
下面展示出的计时器是 用来解释实测工作的 潜在工作时间的 这两个时钟之间的主要区别 来自于它在机器休眠时的行为
对于此类长时间运行的工作 工作可以暂停 就像我们在合上盖子一样 但是 当我们恢复设备运行时 可以看到 ContinuousClock 仍在记录时间 即使在机器休眠的时候 但 SuspendingClock 并没有 通常 这种差异对于 通过暂停执行时间 以确保动画之类的工作 如预期方式完成很关键 如果您需要与机器进行 在时间方面的交互 比如像动画一样 请使用 SuspendingClock
判定和计量 与设备前的人有关的任务 使用 ContinuousClock 则更加合适 因此 如果您需要做 一个绝对持续时间 工作是相对于人类有关的话 就请用 ContinuousClock Swift Async Algorithms 包 使用这些新的 Clock、Instant 和 Duration 类型 来构建通用算法 用于许多与时间相关的事件处理方法 在我们的消息 App 中 我们发现这些功能确实有助于 提供对事件的精确控制 它让我们可以限制交互 并有效的缓冲消息 也许我们利用时间的最突出的领域 是搜索消息 我们创建了一个 管理结果通道的控制器 通道会整理好搜索任务 的结果并返回给我们的 UI 搜索任务本身需要有一些 在时间方面的特征 我们希望确保在服务器上 对搜索消息进行频率限制
Debounce 算法会在迭代过程中 发出下一个值之前 等待一个静止时间 这意味着虽然事件可以很快到来 但我们希望确保 在处理下一个值之前 有一个静默期 当用户搜索时 对搜索框里的文本进行快速更改 我们不希望搜索控制器 对每次搜索内容的修改都发送请求 而是 我们要确保 经过一个静默期 直到我们确定打字的动作 很可能已经完成时 默认情况下 Debounce 算法 将使用 ContinuousClock 在这种情况下 我们可以对输入进行去抖动处理 使它等待指定的 期间不发生任何事的时间段 Clock (时钟) 和 Debounce (持续时间) 不仅用于去抖动 它们也用于其他算法 我们发现真正有用的一个方面 是向服务器发送批量消息 在 Swift 算法包中 有一组算法来对值进行分块 Swift Async Algorithms 包 提供了这些功能 而且还添加了一组 与时钟和持续时间互操作的版本 分块算法系列允许按计数 按时间或按内容控制分块 如果其中任何一个发生错误 则重新抛出该报错 所以我们的代码 在遇到故障时是安全的 我们使用 “chunked(by:)” API 来确保 消息块在一定的持续时间 内被序列化并发送出去 这样 我们的服务器就可以 得到客户端发送的有效数据包 我们能够使用这个 API 以每 500 毫秒的间隔 来构建批量消息 那样的话 如果有人 真的很兴奋并且打字很快 那么发送到服务器的请求 就会被分组 在使用集合和序列时 延迟处理元素 通常是有用且高效的 在 Swift 标准库中 AsyncSequence 与 惰性算法的工作方式非常相似 但就像那些惰性算法一样 很多时候您需要 回到集合的世界 Swift Async Algorithms 包 提供了一组初始化器 用于使用 AsyncSequence 构建集合 这些让您可以用已知限定的 AsyncSequence 异步序列 构建字典、集合或数组 集合初始化器让我们 将转换直接整合在消息初始化 使我们仍然可以用数组作为的数据类型 这真的很有用 因为我们有很多功能 真的可以通过一些更新 来使用 Swift 并发 通过保留我们现有的数据结构 我们可以逐步迁移部分 App 从有意义的地方开始 到目前为止 我们只讨论了一小部分 关于 Swift Async Algorithms 包的亮点 除了我们今天介绍的 还有很多其他内容 我们的算法范围从 组合多个异步序列 按时间限制速率 将事项分块 但这些只是我们最终 在我们的 App 中 广泛使用的亮点功能 这个软件包不仅只有这些 它的范围包括 缓冲 归约、合并 间歇性注入值等等 Swift Async Algorithms 包 采用了一组算法 来处理随时间推移的工作 并将其扩展到一系列 可以在您的 App 中 提供帮助的高级功能 试试看 我们真的很希望看看 您用这些构建了什么 这种兴奋也是共享的 该软件包面向您开放式开发 感谢您的收看 并享受课程的其余部分
-
-
2:01 - The messaging app
struct Account { var messages: AsyncStream<Message> } actor AccountManager { var primaryAccount: Account var secondaryAccount: Account? } protocol MessagePreview { func displayPreviews(_ manager: AccountManager) async }
-
3:16 - Zip
// upload attachments of videos and previews such that every video has a preview that are created concurrently so that neither blocks each other. for try await (vid, preview) in zip(videos, previews) { try await upload(vid, preview) }
-
5:09 - Merge
// Display previews of messages from either the primary or secondary account for try await message in merge(primaryAccount.messages, secondaryAccount.messages) { displayPreview(message) }
-
6:37 - Suspending Clock
// Sleep until a given deadline let clock = SuspendingClock() var deadline = clock.now + .seconds(3) try await clock.sleep(until: deadline)
-
6:56 - Suspending Clock vs. Continuous Clock
let clock = SuspendingClock() let elapsed = await clock.measure { await someLongRunningWork() } //Elapsed time reads 00:05.40 let clock = ContinuousClock() let elapsed = await clock.measure { await someLongRunningWork() } //Elapsed time reads 00:19.54
-
8:34 - Control searching messages
// Control searching messages class SearchController { let searchResults = AsyncChannel<SearchResult>() func search<SearchValues: AsyncSequence>(_ searchValues: SearchValues) where SearchValues.Element == String }
-
9:16 - Debounce
let queries = searchValues .debounce(for: .milliseconds(300)) for await query in queries { let results = try await performSearch(query) await channel.send(results) }
-
10:21 - Chunked by
let batches = outboundMessages.chunked( by: .repeating(every: .milliseconds(500)) ) let encoder = JSONEncoder() for await batch in batches { let data = try encoder.encode(batch) try await postToServer(data) }
-
11:22 - Conversions in initializers
// Create a message with awaiting attachments to be encoded init<Attachments: AsyncSequence>(_ attachments: Attachments) async rethrows { self.attachments = try await Array(attachments) }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。