Swift 联合收割机的订阅(在:选项:)运算符

Swift 联合收割机的订阅(在:选项:)运算符,swift,combine,frp,Swift,Combine,Frp,我有一个关于接线员的问题。如果有人能帮我解决这个问题,我将不胜感激 因此,我们从文件中了解到: 指定要在其上执行订阅、取消和请求操作的计划程序。 与影响下游消息的receive(on:options:)不同,subscribe(on:options:)更改上游消息的执行上下文 另外,我从不同的文章中得到的信息是,除非我们明确指定调度程序在其上接收下游消息(使用receive(on:options:)),否则消息将在用于接收订阅的调度程序上发送 此信息与我在执行过程中实际获得的信息不一致 我有下一

我有一个关于接线员的问题。如果有人能帮我解决这个问题,我将不胜感激

因此,我们从文件中了解到:

指定要在其上执行订阅、取消和请求操作的计划程序。 与影响下游消息的receive(on:options:)不同,subscribe(on:options:)更改上游消息的执行上下文

另外,我从不同的文章中得到的信息是,除非我们明确指定
调度程序
在其上接收下游消息(使用
receive(on:options:)
),否则消息将在用于接收订阅的
调度程序
上发送

此信息与我在执行过程中实际获得的信息不一致

我有下一个代码:

Just("Some text")
    .map { _ in
        print("Map: \(Thread.isMainThread)")
    }
    .subscribe(on: DispatchQueue.global())
    .sink { _ in
        print("Sink: \(Thread.isMainThread)")
    }
    .store(in: &subscriptions)
我预计下一个产出:

Map: false
Sink: false
但我得到的却是:

Map: true
Sink: false
使用
Sequence
publisher时也会发生同样的情况

如果我交换
map
操作符和
subscribe
操作符的位置,我会收到我想要的:

Just("Some text")
    .subscribe(on: DispatchQueue.global())
    .map { _ in
        print("Map: \(Thread.isMainThread)")
    }
    .sink { _ in
        print("Sink: \(Thread.isMainThread)")
    }
    .store(in: &subscriptions)
输出:

Map: false
Sink: false
Map: false
Sink: false
有趣的是,当我在自定义发布服务器上使用第一个列表中相同的运算符顺序时,我会收到我想要的行为:

struct TestJust<Output>: Publisher {
    typealias Failure = Never
    
    private let value: Output
    
    init(_ output: Output) {
        self.value = output
    }
    
    func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
        subscriber.receive(subscription: Subscriptions.empty)
        _ = subscriber.receive(value)
        subscriber.receive(completion: .finished)
    }
}

TestJust("Some text")
    .map { _ in
        print("Map: \(Thread.isMainThread)")
    }
    .subscribe(on: DispatchQueue.global())
    .sink { _ in
        print("Sink: \(Thread.isMainThread)")
    }
    .store(in: &subscriptions)
因此,我认为要么是我完全误解了所有这些机制,要么是一些发布者故意选择线程来发布值(
Just
Sequence
->
Main
URLSession.DataTaskPublisher
->
一些后台
),这对我来说没有意义,原因在这种情况下,我们为什么需要此
订阅(on:options:)


你能帮我理解我错过了什么吗?提前感谢。

首先要了解的是,消息在管道上和管道下流动。流向管道(“上游”)的消息包括:

  • 订阅的实际性能(接收订阅)

  • 订阅服务器向上游发布服务器请求新值

  • 取消消息(这些消息从最终订户向上渗透)

沿管道(“下游”)向下流动的消息包括:

  • 价值观

  • 完成,包括失败(错误)或正常完成(报告发布者发出了其最后一个值)

好的,正如文档中明确指出的,
subscribe(on:)
是关于前者的:向上游流动的消息。但是您实际上并没有跟踪测试中的任何消息,因此您的结果都没有反映任何关于它们的信息!在订阅点上方插入适当的
handleEvents
操作符,以查看管道中向上流动的内容(例如,实现其
receiverRequest:
参数):

同时,您不应该假设消息将在哪个线程上向下游流动(即值和完成)。你说:

另外,我从不同的文章中得到的信息是,除非我们明确指定接收下游消息的调度程序(使用
receive(on:options:)
),否则消息将在用于接收订阅的调度程序上发送

但这似乎是一个虚假的假设。代码中的任何内容都不能明确地确定下游发送线程。正如您正确地说的,您可以通过
receive(on:)
控制这一点,但如果您不这样做,我会说您必须对这件事不做任何假设。某些发布服务器确实会在后台线程上生成值,例如数据任务发布服务器,这非常有意义(数据任务完成处理程序也会发生同样的情况)。其他人则不然

您可以假设除了
receive(on:)
之外的运算符通常不会改变传递线程的值。但是操作员是否以及如何使用订阅线程来确定接收线程,这是您不应该假设的。要控制接收线程,请控制它!调用
receive(on:)
或不假设任何内容

举个例子,如果你把开盘价改为

Just("Some text")
    .receive(on: DispatchQueue.main)
然后您的
map
sink
都将报告它们正在主线程上接收值。为什么?因为你控制了接收线程。无论您在任何
subscribe(on:)
命令中说什么,这都是有效的。它们是完全不同的事情


也许如果您调用
subscribe(on:)
,但不调用
receive(on:)
,那么下游发送线程的某些事情是由
subscribe(on:)
线程决定的,但我肯定不会依赖于有任何硬性的规则;文档中没有这么说!相反,不要这样做。如果您实施了
subscribe(on:)
,也要实施
receive(on:)
,这样您就可以负责所发生的事情。

感谢您提供详细的答案!我的主要误解是理解了“上游”流到底是什么。我在想,上游是不是每个运营商都被放在
订阅(on:)
之前(或之上)。那么,我将重写我的答案,从这个误解开始!我希望我的重写是有用的。我想就一个被问得很好的问题向你致意:非常好的例子。你在提出问题之前做了家庭作业。
Just("Some text")
    .receive(on: DispatchQueue.main)