图解 AsyncSequence 异步序列

共 2921字,需浏览 6分钟

 ·

2021-07-07 15:08

作者丨凌雨
来源丨知识小集(ID:zsxjtip)



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 一样简单,为我们的代码带来更多便捷。

-End-

最近有一些小伙伴,让我帮忙找一些 面试题 资料,于是我翻遍了收藏的 5T 资料后,汇总整理出来,可以说是程序员面试必备!所有资料都整理到网盘了,欢迎下载!

点击👆卡片,关注后回复【面试题】即可获取

在看点这里好文分享给更多人↓↓

浏览 26
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报