º¹⁵/₂₀₂₁ WWDC | 图解 AsyncSequence 异步序列
共 2706字,需浏览 6分钟
·
2021-06-27 05:42
Swift 5.5 为我们带来了 async/await,同时也为我们带来了与之相关的一些新的设计模式和便捷的 API,能让我们更舒服地写代码。基于 async/await 的异步序列就是其中之一。《Meet AsyncSequence》这个 Session 就向我们介绍了异步序列的概念以及使用,一起来看看。
认识异步序列
上面这段代码的目标是下载一个 csv 文件。一个 csv 文件是一个文本文件,每行是一个完整数据行,每行通过指定的分割符组织数据。
下载文件通常是一件耗时的事情,如果数据量很大时,可能需要等待很长时间,因此我们通常会让它异步执行。但有时候我们可能并不想等待所有东西都下载完再展示,而是边下载边展示。所以上面的代码使用了 async/await,即 for-await-in 配合 URL.lines.dropFirst() 函数,在接收到每一行时,去执行想要的操作。
在 《Meet Swift async/await》中可以了解 async/await,在这不多做解释。
这里我们的关注点是 URL.lines 这个属性,文档目前没有对 lines 的详细解释,我们可以看下它的声明:
var lines: AsyncLineSequence<URL.AsyncBytes> { get }
lines 的类型是 AsyncLineSequence,是文本行的异步序列
struct AsyncLineSequence<Base> where Base : AsyncSequence, Base.Element == UInt8
而它继承自 AsyncSequence,也就是我们今天的主角。它是一个协议,提供对其元素的异步、顺序、迭代访问。
// A type that provides asynchronous, sequential, iterated access to its elements.
protocol AsyncSequence
AsyncSequence 会在每个元素上挂起,并在底层迭代器产生值或抛出异常时恢复。
异步序列更像是描述如何随时间产生值,可能产生 0 个或多个值,当迭代器返回 nil 时,表示迭代完成,与普通序列一样。而错误发生时,也会返回 nil 表示结束。
基本原理
如上两图,对于常规迭代,编译器会做一些简单的转换。首先创建一个迭代器变量,然后在 while 循环中调用 next()
而要使用 async/await,编译只做了一个很小的改动,将 makeIterator() 改成 makeAsyncIterator() 即可,这时便可以使用 for-await-in 了。实际上,AsyncSequence 的使用与 Sequence 很相似。
使用
有几种方法可以使用异步序列
• for-await-in
• for-try-await-in:异步序列会招抛出异常;
另外,异步序列中同样可以使用 break 来跳出循环,或者使用 continue 来跳过本次循环
由于异步序列可能抛出异常,所以我们同样可以捕获异常来处理
还可以使用 async 来封装异步序列迭代,让异步序列迭代与其它任务并行。如果异步序列是无限运行的,这怎么处理就很有用。
更重要的是,我们还可以显示终止迭代
新的 AsyncSequence API
除了新加了 AsyncSequence 及相关类外,Swift 5.5 也内置了很多现成的 AsyncSequence API。我们一起来看看。
• FileHandle:从文件中读取通常是异步行为的主要用例,FileHandle 现在有一个新的 bytes 属性,可以访问来自该 FileHandle 的异步字节序列
• URL:URL 新增了一个便利属性,可以从文件或网络中的内容返回 AsyncSequence 行,这将使许多以前非常复杂的任务变得简单和安全。
• URLSession:URLSession 现在有一个 bytes 函数来获取给定 URL 或 URLRequest 的异步字节序列。
• Notification:通知现在也可以使用新的通知 API 来等待通知,可以看到迭代并不是与 AsyncSequence 交互的唯一方式。使用像 firstWhere 这样的方法,连同通知异步序列,允许一些非常简洁的新设计模式,可以使以前表达复杂逻辑的代码现在紧凑且更易于阅读。
当然,还有许多其它新的 API 用于异步操作,以用于处理异步序列的值。
构建自己的异步序列
除了使用 SDK 提供的内置 API 外,我们也可以自定义异步序列。使用异步序列的场景也很广,几乎任何不需要响应并且只是通知出现新值的场景都适合异步序列。
例如下面这个场景,QuakeMonitor 有一个处理程序属性和启动/停止监听方法,很适合使用异步序列。
创建了一个监视器,并分配了一个获取值的处理程序,然后启动监视器,以便可以将地震发送到处理程序。只需少量代码即可构建异步序列。
构造异步流时,指定元素类型和构造闭包。闭包中有一个 yield,可以多次产生值、完成或处理终止。onTermination 可以处理取消和清理。
使用起来也很方便,如结合现有的 filter 方法,或者使用 for-await-in 语句。
上面创建异步序列是基于 AsyncStream,它包括异步序列所有需要所有事情,如安全性、迭代、取消操作、甚至是处理缓冲。
而如果涉及到错误处理的话,则需要使用到 AsyncThrowingStream,它与 AsyncStream 一样,但可以通过从迭代中抛出异常来处理失败。
小结
AsyncSequence 是一个非常强大的工具,可以安全地处理多个异步值。而且使用起来和 Sequence 一样简单,为我们的代码带来更多便捷。
☞ ºº⁷/₂₀₂₁ WWDC | 初识 Xcode Cloud
☞ ºº⁹/₂₀₂₁ WWDC | 性能优化终极生存指南
☞ º¹º/₂₀₂₁ WWDC | 8 分钟优化你的 App Store 产品页
☞ º¹¹/₂₀₂₁ WWDC | App Clips 新特性
分享,收藏,点赞,在看四连,就差您了 👇👇👇