Swift Combine 使用 flatMap 和 catch错误处理 从入门到精通十三

news2024/11/29 11:41:48

Combine 系列

  1. Swift Combine 从入门到精通一
  2. Swift Combine 发布者订阅者操作者 从入门到精通二
  3. Swift Combine 管道 从入门到精通三
  4. Swift Combine 发布者publisher的生命周期 从入门到精通四
  5. Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五
  6. Swift Combine 订阅者Subscriber的生命周期 从入门到精通六
  7. Swift 使用 Combine 进行开发 从入门到精通七
  8. Swift 使用 Combine 管道和线程进行开发 从入门到精通八
  9. Swift Combine 使用 sink, assign 创建一个订阅者 从入门到精通九
  10. Swift Combine 使用 dataTaskPublisher 发起网络请求 从入门到精通十
  11. Swift Combine 用 Future 来封装异步请求 从入门到精通十一
  12. Swift Combine 有序的异步操作 从入门到精通十二
    在这里插入图片描述

1. 错误处理

上述示例都假设,如果发生错误情况,订阅者将处理这些情况。 但是,你并不总是能够控制订阅者的要求——如果你使用 SwiftUI,情况可能如此。 在这些情况下,你需要构建管道,以便输出类型与订阅者的类型匹配。 这意味着你在处理管道内的任何错误。

例如,如果你正在使用 SwiftUI,并且你希望使用 assign 在按钮上设置 isEnabled 属性,则订阅者将有几个要求:

  1. 订阅者应匹配 <Bool, Never> 的类型输出
  2. 应该在主线程调用订阅者

如果发布者抛出一个错误(例如 URLSession.dataTaskPublisher ),你需要构建一个管道来转换输出类型,还需要处理管道内的错误,以匹配错误类型 <Never>

如何处理管道内的错误取决于管道的定义方式。 如果管道设置为返回单个结果并终止, 一个很好的例子就是 使用 catch 处理一次性管道中的错误。 如果管道被设置为持续更新,则错误处理要复杂一点。 这种情况下的一个很好的例子是 使用 flatMap 和 catch 在不取消管道的情况下处理错误。

2.使用 assertNoFailure 验证未发生失败

目的:验证管道内未发生错误

在管道中测试常量时,断言 assertNoFailure 非常有用,可将失败类型转换为 <Never>。 如果断言被触发,该操作符将导致应用程序终止(或测试时导致调试器崩溃)。

这对于验证已经处理过错误的常量很有用。 比如你确信你处理了错误,对管道进行了 map 操作,该操作可以将 <Error> 的失败类型转换为 <Never> 传给所需的订阅者。

更有可能的是,你希望将错误处理掉,而不是终止应用程序。 期待后面的 使用 catch 处理一次性管道中的错误 和 使用 flatMap 和 catch 在不取消管道的情况下处理错误 模式吧,它们会告诉你如何提供逻辑来处理管道中的错误。

3. 使用 catch 处理一次性管道中的错误

目的:如果你需要在管道内处理失败,例如在使用 assign 操作符或其他要求失败类型为 <Never> 的操作符之前,你可以使用 catch 来提供适当的逻辑。

catch 处理错误的方式,是将上游发布者替换为另一个发布者,这是你在闭包中用返回值提供的。

请注意,这实际上终止了管道。 如果你使用的是一次性发布者(不创建多个事件),那这就没什么。

例如,URLSession.dataTaskPublisher 是一个一次性的发布者,你可以使用 catch 在发生错误时返回默认值,以确保你得到响应结果。 扩展我们以前的示例以提供默认的响应:

struct IPInfo: Codable {
    // matching the data structure returned from ip.jsontest.com
    var ip: String
}
let myURL = URL(string: "http://ip.jsontest.com")
// NOTE(heckj): you'll need to enable insecure downloads in your Info.plist for this example
// since the URL scheme is 'http'

let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: myURL!)
    // the dataTaskPublisher output combination is (data: Data, response: URLResponse)
    .map({ (inputTuple) -> Data in
        return inputTuple.data
    })
    .decode(type: IPInfo.self, decoder: JSONDecoder()) 
    .catch { err in 
        return Publishers.Just(IPInfo(ip: "8.8.8.8"))
    }
    .eraseToAnyPublisher()
  1. 通常,catch 操作符将被放置在几个可能失败的操作符之后,以便在之前任何可能的操作失败时提供回退或默认值。
  2. 使用 catch 时,你可以得到错误类型,并可以检查它以选择如何提供响应。
  3. Just 发布者经常用于启动另一个一次性管道,或在发生失败时直接提供默认的响应。

此技术的一个可能问题是,如果你希望原始发布者生成多个响应值,但使用 catch 之后原始管道就已结束了。 如果你正在创建一条对 @Published 属性做出响应的管道,那么在任何失败值激活 catch 操作符之后,管道将不再做出进一步响应。 有关此工作原理的详细信息,请参阅 catch

如果你要继续响应错误并处理它们,请参阅 使用 flatMap 和 catch 在不取消管道的情况下处理错误。

4. 在发生暂时失败时重试

目的: 当 .failure 发生时,retry 操作符可以被包含在管道中以重试订阅。

当向 dataTaskPublisher 请求数据时,请求可能会失败。 在这种情况下,你将收到一个带有 error.failure 事件。 当失败时,retry 操作符将允许你对相同请求进行一定次数的重试。 当发布者不发送 .failure 事件时,retry 操作符会传递结果值。 retry 仅在发送 .failure 事件时才在 Combine 管道内做出响应。

retry 收到 .failure 结束事件时,它重试的方式是给它所链接的操作符或发布者重新创建订阅。

当尝试请求连接不稳定的网络资源时,通常需要 retry 操作符,或者再次请求时可能会成功的情况。 如果指定的重试次数全部失败,则将 .failure 结束事件传递给订阅者。

在下面的示例中,我们将 retrydelay 操作符相结合使用。 我们使用延迟操作符在下一个请求之前使其出现少量随机延迟。 这使得重试的尝试行为被分隔开,使重试不会快速连续的发生。

此示例还包括使用 tryMap 操作符以更全面地检查从 dataTaskPublisher 返回的任何 URL 响应。 服务器的任何响应都由 URLSession 封装,并作为有效的响应转发。 URLSession 不将 404 Not Foundhttp 响应视为错误响应,也不将任何 50x 错误代码视作错误。 使用 tryMap,我们可检查已发送的响应代码,并验证它是 200 的成功响应代码。 在此示例中,如果响应代码不是 200 ,则会抛出一个异常 —— 这反过来又会导致 tryMap 操作符传递 .failure 事件,而不是数据。 此示例将 tryMap 设置在 retry 操作符 之后,以便仅在网站未响应时重新尝试请求。

let remoteDataPublisher = urlSession.dataTaskPublisher(for: self.URL!)
    .delay(for: DispatchQueue.SchedulerTimeType.Stride(integerLiteral: Int.random(in: 1..<5)), scheduler: backgroundQueue) 
    .retry(3) 
    .tryMap { data, response -> Data in 
        guard let httpResponse = response as? HTTPURLResponse,
            httpResponse.statusCode == 200 else {
                throw TestFailureCondition.invalidServerResponse
        }
        return data
    }
    .decode(type: PostmanEchoTimeStampCheckResponse.self, decoder: JSONDecoder())
    .subscribe(on: backgroundQueue)
    .eraseToAnyPublisher()
  1. delay 操作符将流经过管道的结果保持一小段时间,在这个例子中随机选择1至5秒。通过在管道中添加延迟,即使原始请求成功,重试也始终会发生。
  2. 重试被指定为尝试3次。 如果每次尝试都失败,这将导致总共 4 次尝试 - 原始请求和 3 次额外尝试。
  3. tryMap 被用于检查 dataTaskPublisher 返回的数据,如果服务器的响应数据有效,但不是 200 HTTP 响应码,则返回 .failure 完成事件。

使用 retry 操作符与 URLSession.dataTaskPublisher 时,请验证你请求的 URL 如果反复请求或重试,不会产生副作用。 理想情况下,此类请求应具有幂等性。 如果没有,retry 操作符可能会发出多个请求,并产生非常意想不到的副作用。

参考

https://heckj.github.io/swiftui-notes/index_zh-CN.html

代码

https://github.com/heckj/swiftui-notes

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

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

相关文章

vue3 之 商城项目—购物车

购物车业务逻辑梳理拆解 1️⃣整个购物车的实现分为两个大分支&#xff0c;本地购物车操作和接口购物车操作 2️⃣由于购物车数据的特殊性&#xff0c;采取Pinia管理购物车列表数据并添加持久话缓存 本地购物车—加入购物车实现 stores/cartStore.js // 封装购物车模块 imp…

前端秘法基础式(HTML)(第一卷)

目录 一.img标签 1.src属性 2.alt/title/width/height/border属性 二.a标签 1.href属性 2.target属性 三.表格/列表标签 1.表格标签 ​编辑 2.列表标签 2.1无序列表 2.2有序列表 一.img标签 1.src属性 img标签必须搭配src来使用,src用来指定路径 注意这里的src不…

【王道数据结构】【chapter5树与二叉树】【P158t10】

已知二叉树以二叉链表存储&#xff0c;编写算法完成&#xff1a;对于树种每个元素值为x的结点&#xff0c;删除以它为根的子树&#xff0c;并释放相应的空间 #include <iostream> #include <queue> typedef struct treenode{char data;struct treenode *left;struc…

CorelDRAW2024国内专业个人免费版下载

CorelDRAW是一款屡获殊荣的图形和图像编辑软件&#xff0c;包含两个绘图应用程序&#xff1a;一个用于矢量图及页面设计&#xff0c;另一个用于图像编辑。自1989年进入中国市场以来&#xff0c;CorelDRAW不断推出新的版本和功能&#xff0c;以满足用户不断变化的需求。 CorelD…

安装 NVIDIA Chat with RTX

安装 NVIDIA Chat with RTX 0. NVIDIA Chat with RTX 是什么&#xff1f;1. 安装 NVIDIA Chat with RTX2. 使用 NVIDIA Chat with RTX3. NVIDIA Chat with RTX 下载地址 0. NVIDIA Chat with RTX 是什么&#xff1f; Chat With RTX 是一款演示应用程序&#xff0c;可让您个性化…

2.8:Maefile、计算单词个数、判断文件类型、单词逆置

1.有main.c&#xff0c;test.c&#xff0c;test1.c&#xff0c;创建Makefile 程序代码&#xff1a; Makefile: 1 CCgcc2 EXEhello3 OBJS$(patsubst %.c,%.o,$(wildcard *.c))4 CFLAGS-c -o5 all:$(EXE)6 7 #hello依赖test.o main.o8 $(EXE):$(OBJS)9 $(CC) $^ -o $10 …

输出用“*”组成的X形图案。

输出用“*”组成的X形图案 输入描述&#xff1a; 多组输入&#xff0c;一个整数&#xff08;2~20&#xff09;&#xff0c;表示输出的行数&#xff0c;也表示组成“X”的反斜线和正斜线的长度。 输出描述&#xff1a; 针对每行输入&#xff0c;输出用“*”组成的X形图案。 …

波奇学Linux:文件缓冲区

问题导入 文件流输出直接向显示器和重定向文件有不一样的表现 分别向显示器文件输出四个语句&#xff0c;最后fork创建子进程。 当程序运行时和程序重定向到文件中&#xff0c;输出的内容不一样。 重定向时c库函数&#xff08;printf,fprintf,fwrite&#xff09;调用了两次&…

片上网络NoC(3)——拓扑指标

目录 一、概述 二、指标 2.1 与网络流量无关的指标 2.1.1 度&#xff08;degree&#xff09; 2.1.2 对分带宽&#xff08;bisection bandwidth&#xff09; 2.1.3 网络直径&#xff08;diameter&#xff09; 2.2 与网络流量相关的指标 2.2.1 跳数&#xff08;hop coun…

JAVA设计模式之中介者模式详解

中介者模式 1 中介者模式介绍 提到中介模式&#xff0c;有一个比较经典的例子就是航空管制。 为了让飞机在飞行的时候互不干扰&#xff0c;每架飞机都需要知道其他飞机每时每刻的位置&#xff0c;这就需要时刻跟其他飞机通信。飞机通信形成的通信网络就会无比复杂。这个时候&…

【王道数据结构】【chapter5树与二叉树】【P158t6】

二叉树按二叉链表形式存储&#xff0c;试编写一个判别二叉树是否是完全二叉树的算法 #include <iostream> #include <queue> typedef struct treenode{char data;struct treenode *left;struct treenode *right; }treenode,*ptreenode;ptreenode buytreenode(char …

3dmax云渲染怎么使用?渲染100邀请码1a12

本文为大家整理了3dmax云渲染的使用教程&#xff0c;一起来看看吧。案例&#xff1a;渲染效果图&#xff0c;插件方式提交必备工具&#xff1a;渲染100客户端 1、进入渲染100官网(http://www.xuanran100.com/?ycode1a12)注册账号&#xff0c;记得注册时填邀请码1a12&#xff0…

UnityShader——03图形硬件简史与可编程管线

图形硬件简史与可编程管线 GPU发展简史 GPU英文全称Graphic Processing Unit&#xff0c;中文翻译为“图形处理器”&#xff0c;在现代计算机系统中的作用变得越来越重要 20世纪六七十年代&#xff0c;受硬件条件的限制&#xff0c;图形显示器只是计算机输出的一种工具&…

LeetCode、136. 只出现一次的数字【简单,位运算】

文章目录 前言LeetCode、136. 只出现一次的数字【简单&#xff0c;位运算】题目链接与分类思路异或一遍运算 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术…

【蓝桥杯】灭鼠先锋

一.题目描述 二.解题思路 博弈论&#xff1a; 只能转移到必胜态的&#xff0c;均为必败态。 可以转移到必败态的&#xff0c;均为必胜肽。 最优的策略是&#xff0c;下一步一定是必败态。 #include<iostream> #include<map> using namespace std;map<string,bo…

Java之拦截器interceptor

1. 概念 2. 步骤 第一步 第二步 参考资料 https://www.bilibili.com/video/BV1m84y1w7Tb?p168&vd_source705343a89f38d5c0d895383ccf38a5d6

VS Code主题设置(美化VS Code)

主题的具体效果放在了文章末尾&#xff0c;这篇文章后续也会进行更新 目录 切换整体主题&#xff08;整体主题&#xff09; 1.VS Code内置主题&#xff08;快捷键&#xff1a;CtrlK &#xff0c;CtrlT&#xff09; 1.VS Code左上角点击文件 2.选择首选项-->主题-->颜色…

LeetCode、1318. 或运算的最小翻转次数【中等,位运算】

文章目录 前言LeetCode、1318. 或运算的最小翻转次数【中等&#xff0c;位运算】题目链接与分类题解位运算 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领…

Map和Set(哈希表)

目录 map&#xff1a; map说明&#xff1a; Map.Entry的说明&#xff1a;,v> Map 的常用方法: 演示&#xff1a; 注意&#xff1a; TreeMap和HashMap的区别 Set&#xff1a; 常见方法说明&#xff1a; 注意&#xff1a; TreeSet和HashSet的区别 哈希表: 冲突&a…

数模.SI模型SI的四种扩展

一&#xff1a;最简单的考虑方式 二考虑某种使得参数beta降低的因素 三&#xff1a;增加人口自然出生率和死亡率&#xff0c;但不考虑疾病的死亡率 四&#xff1a;不考虑人口自然出生率和死亡率&#xff0c;只考虑疾病的死亡率 五&#xff1a;同时考虑人口自然出生率和死亡率和…