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

2024-02-14 17:44

本文主要是介绍Swift Combine 使用 flatMap 和 catch错误处理 从入门到精通十三,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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.comvar 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 inreturn 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

这篇关于Swift Combine 使用 flatMap 和 catch错误处理 从入门到精通十三的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/709172

相关文章

Spring Boot3虚拟线程的使用步骤详解

《SpringBoot3虚拟线程的使用步骤详解》虚拟线程是Java19中引入的一个新特性,旨在通过简化线程管理来提升应用程序的并发性能,:本文主要介绍SpringBoot3虚拟线程的使用步骤,... 目录问题根源分析解决方案验证验证实验实验1:未启用keep-alive实验2:启用keep-alive扩展建

使用Java实现通用树形结构构建工具类

《使用Java实现通用树形结构构建工具类》这篇文章主要为大家详细介绍了如何使用Java实现通用树形结构构建工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录完整代码一、设计思想与核心功能二、核心实现原理1. 数据结构准备阶段2. 循环依赖检测算法3. 树形结构构建4. 搜索子

GORM中Model和Table的区别及使用

《GORM中Model和Table的区别及使用》Model和Table是两种与数据库表交互的核心方法,但它们的用途和行为存在著差异,本文主要介绍了GORM中Model和Table的区别及使用,具有一... 目录1. Model 的作用与特点1.1 核心用途1.2 行为特点1.3 示例China编程代码2. Tab

SpringBoot使用OkHttp完成高效网络请求详解

《SpringBoot使用OkHttp完成高效网络请求详解》OkHttp是一个高效的HTTP客户端,支持同步和异步请求,且具备自动处理cookie、缓存和连接池等高级功能,下面我们来看看SpringB... 目录一、OkHttp 简介二、在 Spring Boot 中集成 OkHttp三、封装 OkHttp

JavaScript错误处理避坑指南

《JavaScript错误处理避坑指南》JavaScript错误处理是编程过程中不可避免的部分,它涉及到识别、捕获和响应代码运行时可能出现的问题,本文将详细给大家介绍一下JavaScript错误处理的... 目录一、错误类型:三大“杀手”与应对策略1. 语法错误(SyntaxError)2. 运行时错误(R

使用Python实现获取网页指定内容

《使用Python实现获取网页指定内容》在当今互联网时代,网页数据抓取是一项非常重要的技能,本文将带你从零开始学习如何使用Python获取网页中的指定内容,希望对大家有所帮助... 目录引言1. 网页抓取的基本概念2. python中的网页抓取库3. 安装必要的库4. 发送HTTP请求并获取网页内容5. 解

使用Python实现网络设备配置备份与恢复

《使用Python实现网络设备配置备份与恢复》网络设备配置备份与恢复在网络安全管理中起着至关重要的作用,本文为大家介绍了如何通过Python实现网络设备配置备份与恢复,需要的可以参考下... 目录一、网络设备配置备份与恢复的概念与重要性二、网络设备配置备份与恢复的分类三、python网络设备配置备份与恢复实

C#中的 StreamReader/StreamWriter 使用示例详解

《C#中的StreamReader/StreamWriter使用示例详解》在C#开发中,StreamReader和StreamWriter是处理文本文件的核心类,属于System.IO命名空间,本... 目录前言一、什么是 StreamReader 和 StreamWriter?1. 定义2. 特点3. 用

Python使用date模块进行日期处理的终极指南

《Python使用date模块进行日期处理的终极指南》在处理与时间相关的数据时,Python的date模块是开发者最趁手的工具之一,本文将用通俗的语言,结合真实案例,带您掌握date模块的六大核心功能... 目录引言一、date模块的核心功能1.1 日期表示1.2 日期计算1.3 日期比较二、六大常用方法详

Python使用DrissionPage中ChromiumPage进行自动化网页操作

《Python使用DrissionPage中ChromiumPage进行自动化网页操作》DrissionPage作为一款轻量级且功能强大的浏览器自动化库,为开发者提供了丰富的功能支持,本文将使用Dri... 目录前言一、ChromiumPage基础操作1.初始化Drission 和 ChromiumPage