使用异步收集 Publisher 值

2024-05-01

我一直在为我们拥有的一些组合代码编写一些单元测试。我遇到了一些问题。我想我已经简化了这个测试中的各个部分。注意:这不是一个测试 - 这是我试图理解为什么其中一个测试不起作用!

func test_collectingPassthroughValues() async throws {
    // In the real test this is injected in to the unit under test.
    let subject = PassthroughSubject<Int, Never>()

    // I'm expecting this to only complete once `subject` finishes. I've used
    // `async let` so I can poke some data through `subject` and then later on
    // `await collectValues` to hopefully get back the stuff published by 
    // `subject`. In the real test this is a property from the unit under test
    // which runs various operators on `subject`.
    async let collectValues = await subject.values.reduce(into: []) { $0.append($1) }

    // Send some data through `subject` and then `.finish` it.
    subject.send(10)
    subject.send(20)
    subject.send(completion: .finished)

    // Await the values so we can check we got what's expected.
    let values = await collectValues

    // This fails…
    XCTAssertEqual(values, [10, 20])
}

断言失败并显示:

est_collectingPassthroughValues(): XCTAssertTrue failed - Found difference for 
Different count:
 |  Received: (0) []
 |  Expected: (2) [10, 20]

So subject.values似乎什么也没得到;我不知道为什么?

Thanks!


发生的事情相当简单。如何正确编写它还不太清楚,我的建议是“不要这样做”。

首先,一个不是问题的小问题:

async let collectValues = await subject.values.reduce(into: []) { $0.append($1) }

你不应该使用await这里。如果没有其他问题的话,这可能会成为一个问题。

根本问题是,如果没有订阅者,PassthroughSubject 就会丢弃消息。在您当前的代码中,这绝对会发生,但也很难修复。

// Taking out the extra `await`
async let collectValues = subject.values.reduce(into: []) { $0.append($1) }

// That line is pretty close to:
    let collectValues = Task {
        var values: [Int] = []
        for await value in subject.values {
            values.append(value)
        }
        return values
    }

问题是这会启动一项可能不会立即启动的任务。所以你的下一行代码,subject.send(10)没有订阅者(它甚至还没有到达for-await线),然后就被扔掉了。

你可以通过添加一个来修复它try await Task.sleep(for: .seconds(1))创建任务后,但没有多大帮助。 PassthroughSubject 上没有缓冲。当你打电话时append,没有什么在听。该值将被丢弃,并且您将删除 20。

你可以通过缓冲来改善事情,但你仍然需要睡觉(我认为这是不可接受的)。尽管如此,以下内容对我来说非常可靠:

func test_collectingPassthroughValues() async throws {
    // In the real test this is injected in to the unit under test.
    let subject = PassthroughSubject<Int, Never>()
    let readSubject = subject.buffer(size: 10, prefetch: .keepFull, whenFull: .dropOldest)

    async let collectValues = readSubject.values.reduce(into: []) { $0.append($1) }

    try await Task.sleep(for: .seconds(1))
    subject.send(10)
    subject.send(20)
    subject.send(completion: .finished)

    // Await the values so we can check we got what's expected.
    let values = await collectValues

    XCTAssertEqual(values, [10, 20])
}

但在我看来,这是一种完全失败的方法。

我不会尝试将 PassthroughSubject 与.values。我只是看不出有什么方法可以让它变得健壮。更广泛地说,我建议非常小心地混合组合和结构化并发。他们对于事情应该如何运作往往有非常不同的想法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用异步收集 Publisher 值 的相关文章

  • Swift 中的 quitFirstResponder

    我怎样才能用Apple的新语言实现它 Objective C 代码 void touchesBegan NSSet touches withEvent UIEvent event for UIView view in self view s
  • Swift 包管理器:“多个目标名为...”

    我正在尝试构建一个服务器端 Swift Web 应用程序 我的应用程序的基础框架将是Kitura https www kitura io来自IBM 此外 我还想利用AWS SDK Swift https github com noppoMa
  • SwiftUI - 预览时未知的预览提供程序“ContentView_Previews_”。发生在一个全新的项目中

    我有这个简单的观点 import SwiftUI struct ContentView View var body some View Text Hello struct ContentView Previews PreviewProvid
  • 如何在 Swift Playgrounds 中获得弹出对话框

    我想知道如何在 Swift 中弹出一个对话框游乐场 是的 必须在 Playgrounds 中 我尝试了以下代码 直接来自 AppleDevs 站点 然而 无论我如何尝试 self tag always抛出错误 谁能帮我这个 import U
  • 如何从 Firebase 同步检索数据?

    我有两个集合 即用户和问题 根据使用 userId 登录的用户 我检索currQuestion价值来自users收藏 基于currQuestion值 我需要检索question来自 Firebase 的文档Questions收藏 我使用下面
  • 如何在 Swift 中创建 UIAlertView?

    我一直在努力在 Swift 中创建 UIAlertView 但由于某种原因我无法得到正确的语句 因为我收到此错误 找不到接受提供的 init 重载 论点 我是这样写的 let button2Alert UIAlertView UIAlert
  • 进入后台时 Alamofire 请求卡住?

    我正在使用 Alamofire 调用 Web 服务 该服务需要相当长的时间才能加载 如果应用程序进入后台 当我返回应用程序时 我会被加载程序卡住 我想这是因为调用永远不会向我的完成处理程序返回任何内容 我该如何解决这个问题 您可以使用后台抓
  • 二元运算符“/”不能应用于两个(Int)操作数[重复]

    这个问题在这里已经有答案了 我得到了Binary operator cannot be applied to two Int operands当我将以下代码放入 Xcode 中的 Swift Playground 时出错 func sumO
  • 在 UITableView 的部分标题文本下方添加一些边距

    我已经设计了标题文本的样式 func tableView tableView UITableView cellForRowAtIndexPath indexPath NSIndexPath gt UITableViewCell let ce
  • 函数不会等到 Promise 得到解决

    我正在开发一个简单的不和谐机器人 我正在尝试打印有关某个玩家的一些一般数据 我最近了解了 async await 并尝试将其实现到我的代码中 然而 它似乎不起作用 因为当我第一次触发此代码时 它会打印 null 但在后续触发时 它将打印正确
  • 领域:结果 和列表

    是否可以转换Results
  • 如何将自定义 C 代码放入 SwiftPM 包中?

    我正在尝试将 C 代码打包到 Swift 模块中 我们称之为CModule 一旦我将其放入项目的基本文件夹中 Swift模块 并配置了搜索路径 我可以在 Swift 文件中自动完成工作 并检测错误 警告 问题是 导入时它无法识别该模块 并且
  • 如何观察UserDefaults的变化?

    我有一个 ObservedObject在我看来 struct HomeView View ObservedObject var station Station var body some View Text self station sta
  • 如何去掉 UIWebView 上的状态栏背景?

    从 iOS 11 开始 当UIWebView全屏时 状态栏上会出现与屏幕颜色相同的假背景UIWebView背景 有人知道如何摆脱它吗 甚至添加IUWebView到故事板并使其全屏将使状态栏背景出现 我一直在尝试编辑 UIWebView 的大
  • 从 WEB API 控制器的异步方法中返回 Void

    我从这个博客获得的 ASP NET MVC 4 WEB API 控制器中有这个异步方法 http www strathweb com 2012 04 html5 drag and drop asynchronous multi file u
  • 我们能否检测用户是否通过主页按钮或锁定按钮离开而没有监听 darwin 通知?

    我最近向应用程序商店提交了一个新的二进制文件并将其发送以供审核 但它立即被拒绝并显示以下消息 不支持的操作 不允许应用程序监听设备锁定通知 经过一番挖掘后 我发现我们无法使用 com apple springboard lockstate
  • 调用 SwiftUI 中位置 #11、#12 处的额外参数 [重复]

    这个问题在这里已经有答案了 我在 SwiftUI 中的切换开关上不断收到 调用中位置 11 12 处有额外参数 错误 我见过其他人有 调用中的额外参数 错误 但答案似乎没有帮助 另外 我的错误是 位置 11 12 我还没有看到其他人发生这种
  • 以编程方式从底部裁剪图像

    我正在开发自定义相机应用程序 一切进展顺利 但我在从底部裁剪图像时遇到了问题 即 裁剪后的图像与原始图像具有完全相同的宽度 但高度将为原始图像的 1 3 并且必须从底部开始 斯威夫特3解决方案 func cropBottomImage im
  • iOS 防止计时器 UILabel 在数字变化时“晃动”

    我有一个UILabel它以以下格式显示计时器的输出MM ss SS 分 秒 厘秒 但是随着厘秒宽度的变化 它从左向右 摇动 例如 11 比 33 窄 有什么办法可以减轻这种情况吗 我尝试过将其居中 给它固定的宽度 但它们似乎没有帮助 从iO
  • ios - Gamekit 的 GKOctree 未找到元素

    我正在尝试使用GKOctree https developer apple com documentation gameplaykit gkoctree用于高效检索 3D 空间中的对象 然而 以下代码似乎没有按预期工作 import Gam

随机推荐