2025-02-07 18:34:42
,某些文章具有时效性,若有错误或已失效,请在下方留言。AsyncSequence
has implementations of many of the same methods that come with Sequence
, but how they operate varies: some return a single value that fulfills your request, such as requesting the first value from the async sequence, and others return a new kind of async sequence, such as filtering values as they arrive.AsyncSequence
具有许多与 Sequence
相同的方法的实现,但它们的 操作方式 各不相同:有些返回满足请求的单个值,例如从异步序列请求第一个值,而另一些则返回一种新的异步序列,例如在值到达时筛选值。
This distinction in turn affects how they are called: returning a single value requires you to await at the call site, whereas returning a new async sequence requires you to await later on when you’re reading values from the new sequence.
这种区别反过来又会影响它们的调用方式:返回单个值需要你在调用站点等待,而返回新的异步序列需要你稍后在从新序列中读取值时等待。
To demonstrate this difference, let’s try out a few common operations, starting with a call to map()
. Mapping an async sequence creates a new async sequence with the type AsyncMapSequence
, which stores both your original async sequence and also the transformation function you want to use.
为了演示这种差异,让我们尝试一些常见的作,从调用 map()
开始。映射异步序列会创建一个 AsyncMapSequence
类型的新异步序列,该序列存储原始异步序列和要使用的转换函数。
So, instead of waiting for all elements to be returned and transforming them at once, you’ve effectively put the transformation into a chain of work: rather than fetching an item and sending it back, the sequence now fetches an item, transforms it, then sends it back.
因此,您不是等待所有元素返回并立即转换它们,而是有效地将转换放入一个工作链中:序列现在不是获取项目并将其发送回去,而是现在获取一个项目,转换它,然后 将其发送回去。
So, we could map over the lines from a URL
to make each line uppercase, like this:
因此,我们可以映射 URL
中的行,使每行大写,如下所示:
func shoutQuotes() async throws {
let url = URL(string: "https://hws.dev/quotes.txt")!
let uppercaseLines = url.lines.map(\.localizedUppercase)
for try await line in uppercaseLines {
print(line)
}
}
try? await shoutQuotes()
This also works for converting between types using map()
, like this:
这也适用于使用 map()
在类型之间进行转换,如下所示:
struct Quote {
let text: String
}
func printQuotes() async throws {
let url = URL(string: "https://hws.dev/quotes.txt")!
let quotes = url.lines.map(Quote.init)
for try await quote in quotes {
print(quote.text)
}
}
try? await printQuotes()
Alternatively, we could use filter()
to check every line with a predicate, and process only those that pass. Using our quotes, we could print only anonymous quotes like this:
或者,我们可以使用 filter()
来检查带有谓词的每一行,并只处理那些通过的行。使用我们的引号,我们只能打印匿名引号,如下所示:
func printAnonymousQuotes() async throws {
let url = URL(string: "https://hws.dev/quotes.txt")!
let anonymousQuotes = url.lines.filter { $0.contains("Anonymous") }
for try await line in anonymousQuotes {
print(line)
}
}
try? await printAnonymousQuotes()
Or we could use prefix()
to read just the first five values from an async sequence:
或者我们可以使用 prefix()
只从异步序列中读取前五个值:
func printTopQuotes() async throws {
let url = URL(string: "https://hws.dev/quotes.txt")!
let topQuotes = url.lines.prefix(5)
for try await line in topQuotes {
print(line)
}
}
try? await printTopQuotes()
And of course you can also combine these together in varying ways depending on what result you want. For example, this will filter for anonymous quotes, pick out the first five, then make them uppercase:
当然,您也可以根据您想要的结果以不同的方式将它们组合在一起。例如,这将过滤匿名引号,挑选出前 5 个,然后将它们设为大写:
func printQuotes() async throws {
let url = URL(string: "https://hws.dev/quotes.txt")!
let anonymousQuotes = url.lines.filter { $0.contains("Anonymous") }
let topAnonymousQuotes = anonymousQuotes.prefix(5)
let shoutingTopAnonymousQuotes = topAnonymousQuotes.map(\.localizedUppercase)
for try await line in shoutingTopAnonymousQuotes {
print(line)
}
}
try? await printQuotes()
Just like using a regular Sequence
, the order you apply these transformations matters – putting prefix()
before filter()
will pick out the first five quotes then select only the ones that are anonymous, which might produce fewer results.
就像使用常规 Sequence
一样,你应用这些转换的顺序很重要——将 prefix()
放在 filter()
之前会挑选出前五个引号,然后 只选择匿名的引号,这可能会产生更少的结果。
Each of these transformation methods returns a new type specific to what the method does, so calling map()
returns an AsyncMapSequence
, calling filter()
returns an AsyncFilterSequence
, and calling prefix()
returns an AsyncPrefixSequence
.
这些转换方法中的每一个都返回特定于该方法功能的新类型,因此调用 map()
将返回 AsyncMapSequence
,调用 filter()
将返回 AsyncFilterSequence
,调用 prefix()
将返回 AsyncPrefixSequence
。
When you stack multiple transformations together – for example, a filter, then a prefix, then a map, as in our previous example – this will inevitably produce a fairly complex return type, so if you intend to send back one of the complex async sequences you should consider an opaque return type such as some AsyncSequence
.
当您将多个转换堆叠在一起时(例如,一个 filter,然后是一个前缀,然后是一个 map,就像我们前面的例子一样),这将不可避免地产生一个相当复杂的返回类型,因此如果你打算发回其中一个复杂的异步序列,你应该考虑一个不透明的返回类型,比如some AsyncSequence
。
All the transformations so far have created new async sequences and so we haven’t needed to use them with await
, but many also produce a single value. These must use await
in order to suspend until all parts of the sequence have been returned, and may also need to use try
if the sequence is throwing.
到目前为止,所有的转换都创建了新的异步序列,因此我们不需要将它们与 await
一起使用,但许多转换也会产生单个值。这些 必须 使用 await
才能暂停,直到返回序列的所有部分,如果序列正在引发,则可能还需要使用 try
。
For example, allSatisfy()
will check whether all elements in an async sequence pass a predicate of your choosing:
例如,allSatisfy()
将检查异步序列中的所有元素是否都传递了您选择的谓词:
func checkQuotes() async throws {
let url = URL(string: "https://hws.dev/quotes.txt")!
let noShortQuotes = try await url.lines.allSatisfy { $0.count > 30 }
print(noShortQuotes)
}
try? await checkQuotes()
Important: As with regular sequences, in order to return a correct value allSatisfy()
must have fetched every value in the sequence first, and therefore using it with an infinite sequence will never return a value. The same is true of other similar functions, such as min()
, max()
, and reduce()
, so be careful.
重要: 与常规序列一样,为了返回正确的值,allSatisfy()
必须首先获取序列中的每个值,因此将其与无限序列一起使用永远不会返回值。其他类似的函数也是如此,例如 min()
、max()
和 reduce()
,所以要小心。
You can of course combine methods that create new async sequences and return a single value, for example to fetch lots of random numbers, convert them to integers, then find the largest:
当然,你可以组合创建新的异步序列并返回单个值的方法,例如获取大量随机数,将它们转换为整数,然后找到最大的:
func printHighestNumber() async throws {
let url = URL(string: "https://hws.dev/random-numbers.txt")!
if let highest = try await url.lines.compactMap(Int.init).max() {
print("Highest number: \(highest)")
} else {
print("No number was the highest.")
}
}
try? await printHighestNumber()
Or to sum all the numbers:
或者将所有数字相加:
func sumRandomNumbers() async throws {
let url = URL(string: "https://hws.dev/random-numbers.txt")!
let sum = try await url.lines.compactMap(Int.init).reduce(0, +)
print("Sum of numbers: \(sum)")
}
try? await sumRandomNumbers()