Swift AsyncSequence — 代码实例详解

news2024/11/24 10:04:13

前言

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 的工作原理。

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

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,我们使用内部类型解决了这个问题。我们可以决定重写此示例以消除对内部类型的需求:

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 开始迭代:

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

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

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

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

    guard current <= limit else {
        return nil
    }

    let result = current
    current += 1
    return result
}

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

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

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

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

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

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 等方法。

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

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

结论

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/494575.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022 Jiangsu Collegiate Programming Contest A. PENTA KILL!

题目链接 Sample 1 Input 10 Bin Guigo Grevthar Bin GALA Grevthar GALA TitaN GALA Guigo GALA Aegis GALA Jojo GALA Grevthar Xiaohu Grevthar GALA Aegis Output PENTA KILL! Sample 3 Input 7 GALA Jojo Aegis Ming GALA Grevthar GALA Grevthar GALA Aegis GALA Guigo…

树莓派硬件介绍及配件选择

目录 树莓派Datasheet下载地址&#xff1a; Raspberry 4B 外观图&#xff1a; 技术规格书&#xff1a; 性能介绍&#xff1a; 树莓派配件选用 电源的选用&#xff1a; 树莓派外壳选用&#xff1a; 内存卡/U盘选用 树莓派Datasheet下载地址&#xff1a; Raspberry Pi …

CompletableFutrue异步处理

异步处理 一、线程的实现方式 1. 线程的实现方式 1.1 继承Thread class ThreadDemo01 extends Thread{Overridepublic void run() {System.out.println("当前线程:" Thread.currentThread().getName());} }1.2 实现Runnable接口 class ThreadDemo02 implements …

故障:更新后电脑卡顿

前一天下班的时候关电脑&#xff0c;关机选项变成了“更新并关机”&#xff0c;没多想&#xff0c;我点了。。。。早上上班就发现电脑卡得不行&#xff0c;看了下更新日志&#xff0c;装了一大堆东西&#xff0c;看了下任务管理器&#xff0c;内存直接跑到了90%&#xff0c;这电…

每日学术速递5.2

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.DataComp: In search of the next generation of multimodal datasets 标题&#xff1a;DataComp&#xff1a;寻找下一代多模态数据集 作者&#xff1a;Samir Yitzhak Gadre, Gab…

【JAVA模块六------ 综合案例基础巩固】

JAVA模块六------ 综合案例基础巩固 1 逢7跳过2 数组元素求和3 判断两个数组的内容是否相同4 查找某个数组元素索引5 数组元素反转输出&#xff1a;&#xff08;逆序输出&#xff09;6 评委打分7 随机产生验证码其他&#xff1a;方法抽取&#xff1a; 1 逢7跳过 要求&#xff1…

国产光伏仪器 6581太阳能电池板伏安特性测试仪

6581太阳能电池板伏安特性测试仪主要用于太阳能电池板生产的最终测试&#xff0c;也可以作为层压前测试使用&#xff0c;能大大提高一次封装成品率。该测试仪适合于单晶、多晶、薄膜等多种电池组件&#xff0c;可进行I-V曲线、P-V曲线、短路电流、开路电压、峰值功率等全部参数…

Hive SQL on Flink 构建流批一体引擎

摘要&#xff1a;本文整理自阿里巴巴开发工程师罗宇侠、阿里巴巴开发工程师方盛凯&#xff0c;在 Flink Forward Asia 2022 流批一体专场的分享。本篇内容主要分为五个部分&#xff1a; 1. 构建流批一体引擎的挑战 2. Hive SQL on Flink 3. 流批一体引擎的收益 4. Demo 5. 未来…

做到“有效沟通”,帮你达到这3个目的

在项目管理中&#xff0c;团队沟通是至关重要的。团队成员之间应该建立良好的沟通机制&#xff0c;及时沟通和协调问题&#xff0c;避免出现问题后甩锅的情况。 在实际项目中&#xff0c;很多问题出现的原因是团队沟通不畅&#xff0c;项目经理需要加强团队沟通的重要性&…

K8S二进制安装报错及各个组件功能介绍

目录 一、K8S安装二、安装时遇到的几个问题2.1、Unable to connect to the server: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "kuberne…

C语言复习笔记2

1.变量命名只能以数字、字母、下划线组成并且不能以数字开头。 #include<stdio.h> #include<unistd.h>//变量名只能由数字字母下划线组成&#xff0c;不能以数字开头 int main() {//int 2b;return 0; }2.内存中保存的是补码 0的补码取反得补码再求源码是-1。 源码…

提升论文影响力的方法

论文发表后&#xff0c;还有一些重要的工作去做&#xff0c;那就是去积极宣传和推广自己的论文&#xff0c;提高自己论文的影响力。这类似于一个电影上映后&#xff0c;主演们还得去做宣传一样&#xff0c;要想办法推销自己的作品。本文将介绍提升论文影响力的方法。 1. 开源数…

xray简单使用指南

前言收到需求如下 用户还需要一个报告 询问了群里的小伙伴推荐使用xray进行扫描 一、下载 https://github.com/chaitin/xray/releases windows下载amd64即可 解压后进入其目录下执行exe程序&#xff0c;帮我们生成一些yaml文件 生成 ca 证书 .\xray_windows_amd64.exe g…

创客匠人:五月,爱成长的力量,有爱必赢

2023年5月4日&#xff0c;创客匠人第六十九届铁军训练营在厦门举办&#xff0c;由创客匠人CEO蒋洪波、CSO张潇峰及HRM何巧婷为厦门总部的伙伴们带来精彩的回顾总结及主题分享。 一、四月总结 为了明确前进奋斗的方向&#xff0c;进一步提升团队战斗力&#xff0c;从而更好地帮助…

Altium Designer中如何在顶层中添加对应端口

转载说明&#xff1a; 大众深度科普 https://jingyan.baidu.com/article/c33e3f4889f327ea15cbb584.html 版权归原作者所有&#xff1b;感谢原作者的分享&#xff1b; 转载到此&#xff0c;主要为了后期查看方便&#xff1b; 本经验简要介绍Altium Designer中如何在顶层中添…

SpringBoot【开发实用篇】---- 热部署

SpringBoot【开发实用篇】---- 热部署 1. 手动启动热部署2. 自动启动热部署3. 参与热部署监控的文件范围配置4. 关闭热部署 什么是热部署&#xff1f;简单说就是你程序改了&#xff0c;现在要重新启动服务器&#xff0c;嫌麻烦&#xff1f;不用重启&#xff0c;服务器会自己悄悄…

人类创新发展的四个阶段:三个核心和一个扩展

纵观人类的发展史&#xff0c;始终伴随着人类的创新过程&#xff0c;这也是人类与其他生物体的最大的区别&#xff0c;别的生物体也就是可以使用工具或者模仿别的生物的动作来制造简单的工具&#xff0c;对工具进行简单的拼接&#xff0c;只有人类是可以进行真正的创造出这个自…

记录-Symbol学习笔记

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 Symbol是JavaScript中的原始数据类型之一&#xff0c;它表示一个唯一的、不可变的值&#xff0c;通常用作对象属性的键值。由于Symbol值是唯一的&#xff0c;因此可以防止对象属性被意外地覆盖或修改。…

Word下划线怎么打?速速get这5个实用方法!

案例&#xff1a;Word下划线怎么打&#xff1f; 【朋友们&#xff0c;最近在写毕业论文&#xff0c;封面文字的下划线打了好久都打不出来&#xff0c;请问大家Word下划线是怎么打的呀&#xff1f;】 在Microsoft Word中&#xff0c;打下划线是一种常见的操作&#xff0c;它可…

Python小姿势 - ## Python与数据库

Python与数据库 简介 当今&#xff0c;数据库是计算机应用中最重要的部分。几乎所有的大型应用都要用到数据库&#xff0c;比如银行、电商、航空、政府、医疗、教育、科研等。数据库的目的是存储数据&#xff0c;并且能够根据用户的需求提供数据。 数据库管理系统&#xff08;D…