How to loop over an AsyncSequence using for await 如何使用 for await 遍历 AsyncSequence

How to loop over an AsyncSequence using for await 如何使用 for await 遍历 AsyncSequence

温馨提示:本文最后更新于2025-02-07 18:13:50,某些文章具有时效性,若有错误或已失效,请在下方留言

You can loop over an AsyncSequence using Swift’s regular loop types, forwhile, 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 的常规循环类型 forwhile 和 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()
下载图标
how-to-loop-over-an-asyncsequence-using-for-await-1.zip
zip文件
50.4K

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()
下载图标
how-to-loop-over-an-asyncsequence-using-for-await-2.zip
zip文件
50.5K

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()
下载图标
how-to-loop-over-an-asyncsequence-using-for-await-3.zip
zip文件
50.5K
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享