2025-02-07 18:13:50
,某些文章具有时效性,若有错误或已失效,请在下方留言。You can loop over an AsyncSequence
using Swift’s regular loop types, for
, while
, and repeat
, but whenever you read a value from the async sequence you must use either await
or try await
depending on whether it can throw errors or not.
你可以使用 Swift 的常规循环类型 for
、while
和 repeat
来遍历 AsyncSequence
,但是每当你从异步序列中读取一个值时,你都必须使用 await
或 try await
,这取决于它是否会抛出错误。
As an example, URL
has a built-in lines
property that generates an async sequence of all the lines directly from a URL. This does a lot of work internally: making the network request, fetching part of the data, converting it into a string, sending it back for us to use, then repeating fetch, convert, and send again and again until the server stops sending back data.
例如,URL
具有内置的 lines
属性,该属性直接从 URL 生成所有行的异步序列。这在内部做了 很多 工作:发出网络请求,获取部分数据,将其转换为字符串,将其发送回给我们使用,然后一次又一次地重复获取、转换和发送,直到服务器停止发回数据。
All that functionality boils down to just a handful of lines of code:
所有这些功能都归结为几行代码:
func fetchUsers() async throws {
let url = URL(string: "https://hws.dev/users.csv")!
for try await line in url.lines {
print("Received user: \(line)")
}
}
try? await fetchUsers()
Notice how we must use try
along with await
, because fetching data from the network might throw errors.
请注意我们必须如何将 try
与 await
一起使用,因为从网络获取数据可能会引发错误。
Using lines
returns a specialized type called AsyncLineSequence
, which returns individual lines from the download as strings. Because our source happens to be a comma-separated values file (CSV), we can write code to read the values from each line easily enough:
Using lines
返回一个名为 AsyncLineSequence
的专用类型,该类型将下载中的各个行作为字符串返回。因为我们的源恰好是一个逗号分隔值文件 (CSV),所以我们可以很容易地编写代码来读取每一行的值:
func printUsers() async throws {
let url = URL(string: "https://hws.dev/users.csv")!
for try await line in url.lines {
let parts = line.split(separator: ",")
guard parts.count == 4 else { continue }
guard let id = Int(parts[0]) else { continue }
let firstName = parts[1]
let lastName = parts[2]
let country = parts[3]
print("Found user #\(id): \(firstName) \(lastName) from \(country)")
}
}
try? await printUsers()
Just like a regular sequence, using an async sequence in this way effectively generates an iterator then calls next()
on it repeatedly until it returns nil
, at which point the loop finishes.
就像常规序列一样,以这种方式使用异步序列会有效地生成一个迭代器,然后重复调用它 next()
,直到它返回 nil
,此时循环结束。
If you want more control over how the sequence is read, you can of course create your own iterator then call next()
whenever you want and as often as you want. Again, it will send back nil
when the sequence is empty, at which point you should stop calling it.
如果你想更好地控制序列的读取方式,你当然可以创建自己的迭代器,然后随时随地调用 next()
。同样,当序列为空时,它将返回 nil
,此时您应该停止调用它。
For example, we could read the first user from our CSV and treat them specially, then read the next four users and do something specific for them, then finally reduce all the remainder down into an array of other users:
例如,我们可以从 CSV 中读取第一个用户并对其进行特殊处理,然后读取接下来的四个用户并为他们做一些特定的作,最后将所有剩余用户缩减为其他用户的数组:
func printUsers() async throws {
let url = URL(string: "https://hws.dev/users.csv")!
var iterator = url.lines.makeAsyncIterator()
if let line = try await iterator.next() {
print("The first user is \(line)")
}
for i in 2...5 {
if let line = try await iterator.next() {
print("User #\(i): \(line)")
}
}
var remainingResults = [String]()
while let result = try await iterator.next() {
remainingResults.append(result)
}
print("There were \(remainingResults.count) other users.")
}
try? await printUsers()