Swift AsyncSequence — 代码实例详解

news/2024/7/20 20:16:40 标签: swift, 开发语言, ios

文章目录

    • 前言
    • 什么是 AsyncSequence?
    • 创建 AsyncSequence
    • 异步序列的迭代
    • 结论

前言

AsyncSequence 是并发性框架和 SE-298 提案的一部分。它的名字意味着它是一个提供异步、顺序和迭代访问其元素的类型。换句话说:它是我们在 Swift 中熟悉的常规序列的一个异步变体。

就像你不会经常创建你的自定义序列一样,我不期望你经常创建一个自定义的 AsyncSequence 实现。然而,由于与 AsyncThrowingStream和AsyncStream 等类型一起使用,你很可能不得不与异步序列一起工作。因此,我将指导你使用 AsyncSequence 实例进行工作。

在这里插入图片描述

什么是 AsyncSequence?

AsyncSequence 是我们在Swift中熟悉的 Sequence 的一个异步变体。由于它的异步性,我们需要使用 await 关键字,因为我们要处理的是异步定义的方法。如果你没有使用过 async/await,我鼓励你阅读我的文章:Swift 中的async/await ——代码实例详解

值可以随着时间的推移而变得可用,这意味着一个 AsyncSequence 在你第一次使用它时可能不包含也可能包含一些,或者全部的值。

重要的是要理解 AsyncSequence 只是一个协议。它定义了如何访问值,但并不产生或包含值。AsyncSequence 协议的实现者提供了一个 AsyncIterator,并负责开发和潜在地存储值。

FunctionNote
contains(_ value: Element) async rethrows -> BoolRequires Equatable element
contains(where: (Element) async throws -> Bool) async rethrows -> BoolThe async on the closure allows optional async behavior, but does not require it
allSatisfy(_ predicate: (Element) async throws -> Bool) async rethrows -> Bool
first(where: (Element) async throws -> Bool) async rethrows -> Element?
min() async rethrows -> Element?Requires Comparable element
min(by: (Element, Element) async throws -> Bool) async rethrows -> Element?
max() async rethrows -> Element?Requires Comparable element
max(by: (Element, Element) async throws -> Bool) async rethrows -> Element?
reduce<T>(_ initialResult: T, _ nextPartialResult: (T, Element) async throws -> T) async rethrows -> T
reduce<T>(into initialResult: T, _ updateAccumulatingResult: (inout T, Element) async throws -> ()) async rethrows -> T

对于这些函数,我们首先定义一个符合 AsyncSequence 协议的类型。该名称是模仿现有的标准库“序列”类型,如 LazyDropWhileCollectionLazyMapSequence 。然后,我们在 AsyncSequence 的扩展中添加一个函数,该函数创建新类型(使用’ self ‘作为’ upstream ')并返回它。

Function
map<T>(_ transform: (Element) async throws -> T) -> AsyncMapSequence
compactMap<T>(_ transform: (Element) async throws -> T?) -> AsyncCompactMapSequence
flatMap<SegmentOfResult: AsyncSequence>(_ transform: (Element) async throws -> SegmentOfResult) async rethrows -> AsyncFlatMapSequence
drop(while: (Element) async throws -> Bool) async rethrows -> AsyncDropWhileSequence
dropFirst(_ n: Int) async rethrows -> AsyncDropFirstSequence
prefix(while: (Element) async throws -> Bool) async rethrows -> AsyncPrefixWhileSequence
prefix(_ n: Int) async rethrows -> AsyncPrefixSequence
filter(_ predicate: (Element) async throws -> Bool) async rethrows -> AsyncFilterSequence

创建 AsyncSequence

创建一个自定义的 AsyncSequence。

为了更好地理解 AsyncSequence 是如何工作的,我将演示一个实现实例。然而,在定义你的 AsyncSequence 的自定义实现时,你可能想用 AsyncStream 来代替,因为它的设置更方便。因此,这只是一个代码例子,以更好地理解 AsyncSequence 的工作原理。

下面的例子沿用了原始提案中的例子,实现了一个计数器。这些值可以立即使用,所以对异步序列没有太大的需求。然而,它确实展示了一个异步序列的基本结构:

swift">struct Counter: AsyncSequence {
    typealias Element = Int

    let limit: Int

    struct AsyncIterator : AsyncIteratorProtocol {
        let limit: Int
        var current = 1
        mutating func next() async -> Int? {
            guard !Task.isCancelled else {
                return nil
            }

            guard current <= limit else {
                return nil
            }

            let result = current
            current += 1
            return result
        }
    }

    func makeAsyncIterator() -> AsyncIterator {
        return AsyncIterator(howHigh: limit)
    }
}

如您所见,我们定义了一个实现 AsyncSequence 协议的 Counter 结构体。该协议要求我们返回一个自定义的 AsyncIterator,我们使用内部类型解决了这个问题。我们可以决定重写此示例以消除对内部类型的需求:

swift">struct Counter: AsyncSequence, AsyncIteratorProtocol {
    typealias Element = Int

    let limit: Int
    var current = 1

    mutating func next() async -> Int? {
        guard !Task.isCancelled else {
            return nil
        }

        guard current <= limit else {
            return nil
        }

        let result = current
        current += 1
        return result
    }

    func makeAsyncIterator() -> Counter {
        self
    }
}

我们现在可以将 self 作为迭代器返回,并保持所有逻辑的集中。

注意,我们必须通过提供 typealias 来帮助编译器遵守 AsyncSequence 协议。

next() 方法负责对整体数值进行迭代。我们的例子归结为提供尽可能多的计数值,直到我们达到极限。我们通过对 Task.isCancelled 的检查来实现取消支持。

请添加图片描述

异步序列的迭代

现在我们知道了什么是 AsyncSequence 以及它是如何实现的,现在是时候开始迭代这些值了。

以上述例子为例,我们可以使用 Counter 开始迭代:

swift">for await count in Counter(limit: 5) {
    print(count)
}
print("Counter finished")

// Prints:
// 1
// 2
// 3
// 4
// 5
// Counter finished

我们必须使用 await 关键字,因为我们可能会异步接收数值。一旦不再有预期的值,我们就退出for循环。异步序列的实现者可以通过在 next() 方法中返回 nil 来表示达到极限。在我们的例子中,一旦计数器达到配置的极限,或者迭代取消,我们就会达到这个预期:

swift">mutating func next() async -> Int? {
    guard !Task.isCancelled else {
        return nil
    }

    guard current <= limit else {
        return nil
    }

    let result = current
    current += 1
    return result
}

许多常规的序列操作符也可用于异步序列。其结果是,我们可以以异步的方式执行映射和过滤等操作。

例如,我们可以只对偶数进行过滤:

swift">for await count in Counter(limit: 5).filter({ $0 % 2 == 0 }) {
    print(count)
}
print("Counter finished")

// Prints: 
// 2
// 4
// Counter finished

或者我们可以在迭代之前将计数映射为一个 String

swift">let counterStream = Counter(limit: 5)
    .map { $0 % 2 == 0 ? "Even" : "Odd" }
for await count in counterStream {
    print(count)
}
print("Counter finished")

// Prints:
// Odd
// Even
// Odd
// Even
// Odd
// Counter finished

我们甚至可以使用 AsyncSequence 而不使用for循环,通过使用 contains 等方法。

swift">let contains = await Counter(limit: 5).contains(3)
print(contains) // Prints: true

注意,上述方法是异步的,意味着它有可能无休止地等待一个值的存在,直到底层的 AsyncSequence 完成。

结论

AsyncSequence 是我们在Swift中熟悉的常规 Sequence 的异步替代品。就像你不会经常自己创建一个自定义 Sequence 一样,你也不太可能创建自定义的异步序列。


http://www.niftyadmin.cn/n/368357.html

相关文章

Oracle数据库从入门到精通系列之十:基于Docker部署Oracle数据库19c的详细步骤

Oracle数据库从入门到精通系列之十:基于Docker部署Oracle数据库19c的详细步骤 一、下载Oracle数据库19c镜像二、查看Oracle数据库19c的镜像三、创建Oracle数据库19c的数据目录四、启动Oracle19c数据库容器五、查看Oracle19c数据库容器启动日志六、查看密码修改脚本七、修改sys…

【算法】单调栈问题

文章目录 题目思路分析代码实现 题目 给定一个不含有重复值的数组arr&#xff0c;找到每一个i位置左边和右边离i位置最近且值比arr[i]小的位置&#xff0c;返回所有位置相应的消息。 比如arr{3&#xff0c;4&#xff0c;1&#xff0c;5&#xff0c;6&#xff0c;2&#xff0c;…

使用sklearn进行机器学习案例(1)

文章目录 案例一. 加州房价预测案例二. MNIST手写数字识别案例三. 波士顿房价预测 案例一. 加州房价预测 线性回归通过对训练集进行训练&#xff0c;拟合出一个线性方程&#xff0c;使得预测值与实际值之间的平均误差最小化。这个过程可以使用梯度下降法等优化算法来实现。即通…

docker版jxTMS使用指南:python服务之jxLocalStateMachine

本文讲解4.0版jxTMS中python服务的jxLocalStateMachine模块&#xff0c;整个系列的文章请查看&#xff1a;docker版jxTMS使用指南&#xff1a;4.0版升级内容 docker版本的使用&#xff0c;请参考&#xff1a;docker版jxTMS使用指南 jxLocalStateMachine提供了一个简单可靠的有…

认识http协议---3

hi,大家好,今天为大家带来http协议的相关知识 &#x1f347;1.http状态响应码 &#x1f347;2.构造http请求 1.直接在地址栏里输入一个URL 2.html的一些特殊标签,触发get请求 3.提交form表单,可以触发get请求和post请求 4.使用ajax &#x1f347;3.再次谈同步和异步 &#x1f3…

跨域问题及解决方案

跨域问题是指在前端Web开发中&#xff0c;不同源的网页之间无法相互访问或通信的问题。产生跨域问题的原因是由于浏览器的同源策略&#xff08;Same Origin Policy&#xff09;所导致的。 同源策略是浏览器的一种安全机制&#xff0c;限制了来自不同源&#xff08;域名、端口、…

【程序人生】上海城市开发者社区小聚有感

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

GPT怎样教我用Python进行数据可视化

文章目录 GPT怎样教我用Python进行数据可视化matplotlibpyecharts总结 GPT怎样教我用Python进行数据可视化 &#x1f680;&#x1f680;首先&#xff0c;我们先看一下这段代码&#xff0c;这是我之前写来读取excel文件中xx大学在各个类别中的获奖情况&#xff0c;并保存在一个…