我有一个PassthroughSubject,它发送30个整数,后跟完成消息。
从受试者那里得到这些数字后,我产生了一个会睡一秒钟的未来,并以输入数字* 2结束。
我使用.receiveOn来确保期货同时运行,但这意味着完成消息也同时通过链传播 并在所有期货交易完成之前结束汇。
那里的任何RxSwift / Combine向导都知道如何做到这一点,因此期货完成会延迟收到完成消息?
这是一个实现上述行为的游乐场:
import Foundation
import Combine
import PlaygroundSupport
/// Setting up the playground
PlaygroundPage.current.needsIndefiniteExecution = true
/// Injects numbers 0-30 into combine message stream, and then sends a finish.
func publishNumbers(to subject: PassthroughSubject<Int, Error>) {
(0..<30).forEach {
subject.send($0)
}
subject.send(completion: .finished)
}
/// Delays for one secont, and completes the future by doubling the input.
func delayAndDoubleNumber(_ int: Int) -> Future<Int, Error> {
return Future<Int, Error> { complete in
sleep(1)
complete(.success(int * 2))
}
}
// Properties involved in Combine processing chain.
let numbersSubject = PassthroughSubject<Int, Error>()
let processingQueue = DispatchQueue.global(qos: .userInitiated)
// Combine processing chain
numbersSubject
.receive(on: processingQueue) //Comment this line to observe that all futures finish, and are collected before the finish message kills the sink.
.flatMap { number in
return delayAndDoubleNumber(number)
}
.collect(4)
.sink(receiveCompletion: { completion in
print("Complete: \(completion)")
}, receiveValue: { value in
print("Received Value: \(value)")
})
publishNumbers(to: numbersSubject)
答案 0 :(得分:3)
从Xcode 11 beta 3开始,您不能将并发队列与Combine一起使用。您应该能够使用Xcode 11 GM。
Philippe Hausler是一位Apple工程师,从事Combine的工作。他在the official Swift forum上说了以下话:
另外,值得注意的是,用作调度程序的
DispatchQueue
必须始终是串行的,以遵守Combine运营商的合同。
Then later他说:
因此,在此进行跟进时,在传播下游事件的方式方面存在一些变化。现在,即使DispatchQueue是并发的,或者OperationQueue不是maxConcurrentOperations的限制为1,或者任何有效的调度程序都在并发,我们现在也能够满足1.03的约束。我们将始终在
.receive(on:)
的请求的调度程序上发送序列化事件。我们稍微偏离规范的一个剩余警告是,我们世界中的上游事件(例如cancel()
和request(_:)
可以同时发生。话虽如此,我们确实以线程安全的方式处理它们。
您可以通过在Future
闭包内调度到并发队列,然后返回主队列,来使并发在Xcode 11 beta 3中起作用:
import Foundation
import Combine
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
func delayAndDoubleNumber(_ int: Int) -> Future<Int, Never> {
return Future<Int, Never> { complete in
DispatchQueue.global(qos: .userInitiated).async {
sleep(1)
DispatchQueue.main.async {
complete(.success(int * 2))
}
}
}
}
let subject = PassthroughSubject<Int, Never>()
subject
.flatMap { delayAndDoubleNumber($0) }
.collect(4)
.sink(
receiveCompletion: { print("Complete: \($0)") },
receiveValue: { print("Received Value: \($0)") })
let canceller = (0 ..< 30).publisher().subscribe(subject)
答案 1 :(得分:1)
免责声明,这可能是对文档的错误解释,但我认为您应该使用receive(on:)
运算符而不是numbersSubject
。
与receive(on:options :)会影响下游消息相反,subscribe(on :)会更改上游消息的执行上下文。
我对此的解释是,如果您希望将subscribe(on:)
中的事件发送到队列中,则可以使用numbersSubject
.flatMap { number in
return delayAndDoubleNumber(number)
}
.collect(4)
.subscribe(on: processingQueue)
.receive(on: RunLoop.main)
.sink(receiveCompletion: { completion in
print("Complete: \(completion)")
}, receiveValue: { value in
print("Received Value: \(value)")
})
,例如:
HTTP connector configuration:
...
Connector connector = new MyTomcatConnector(MyHttp11NioProtocol.class.getName());
connector.setProperty("address", "xxx.xxx.xxx.xxx");
connector.setPort(80);
connector.setRedirectPort(443);
... set addition connector properties(connector);
...
public class MyConnector extends org.apache.catalina.connector.Connector {
...
public MyTomcatConnector(String protocol) throws Exception {
super(protocol);
+ my own logger;
}
...
public MyRequest createRequest() {
my logger
MyRequest request = new MyRequest(this);
return (request);
}
}
public class MyRequest extends org.apache.catalina.connector.Request {
...
public MyRequest(Connector connector) {
super(connector);
+ my own logger;
}
@Override
public Locale getLocale() {
+ my own logger;
Locale locale = super.getLocale();
....
return locale;
}
}
public class MyHttp11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel> {
...
public MyHttp11NioProtocol(){
super(new MyNioEndpoint(HttpInterface.SERVICE));
+ my own logger;
}
...
Copy/paste method from Http11NioProtocol
}
public class MyNioEndpoint extends NioEndpoint implements TLSContextObserver {
protected MySSLContext context;
protected SSLEngine engine;
...
public MyNioEndpoint(String sslContextName) {
super();
...
try {
context = //get instance of my own SSLContext by name (sslContextName);
+ logger
}catch(...)...
}
@Override
protected SSLEngine createSSLEngine(String sniHostName, List<Cipher> clientRequestedCiphers, List<String> clientRequestedApplicationProtocols) {
engine = context.getSSLEngine();
context.addObserver(this);
return engine;
}
}
public class MySSLContext extends AbstractObservable<TLSContextObserver, TLSInterfaceConfig> implements org.apache.tomcat.util.net.SSLContext {
//configuing standart SSLContext (keyManager[], TrusteManager[] ... etc...
}