Combine的未来永远不会完成,当平面映射时



>我有以下简单的Future

class ViewModel {
var cancellables = Set<AnyCancellable>()
func test() {
let trigger = PassthroughSubject<Void, Error>()
let future =  Future<String, Error> { promise in
promise(.success("Future Succeded"))
}
trigger
.flatMap { future }
.sink(receiveCompletion: { completion in
print("completion received (completion)")
}, receiveValue: { val in
print("value received (val)")
})
.store(in: &cancellables)
trigger.send(())
}
}

我不知道为什么当与另一个发布者(在本例中为PassthroughSubject(平面映射时它永远不会完成,它只产生值。

当它不是平面映射时,它会生成值并正常完成。

这种行为可能看起来很奇怪,但它很有意义。完成Future不会完成PassthroughSubject。因此,您可以继续通过PassthroughSubject发送值,这将导致创建新的Future实例并触发。通常,一个Publisher只能完成或出错一次。因此,如果完成Future会触发sink完成闭包,这意味着PassthroughSubject不能再产生新值,这是不可取的,因为PassthroughSubject通常永远不会完成(除非你直接告诉它(。

与您的示例类似,此代码也只触发一次完成:

var cancellables = Set<AnyCancellable>()
(0..<2).publisher
.flatMap { _ in return (0..<5).publisher }
.sink(receiveCompletion: { completion in
print("completion received (completion)")
}, receiveValue: { val in
print("value received (val)")
})
.store(in: &cancellables)

原因是创建的发布者是将发布两个值的发布者,然后完成。如果flatMap发布者会导致调用sink完成,则意味着(0..<2)发布者完成,只是它仍有要发送的值,因此未完成。

长话短说,开始发布者决定流何时完成,而不是平面映射发布者。

我以为我遇到了这个问题,但事实证明这只是因为我没有保留发布者的返回值。如sink()文档中所述

应保留返回值,否则流将被取消。

我的游乐场代码:

var t:Timer?
print(Date())
let _ = Just(33)
.flatMap { (i) -> Future<Int, Never> in
return Future<Int, Never> { promise in
t = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { (t) in
print("Timer!")
promise(.success(i * i))
}
}
}
.sink { (i) in
print("ok")
print("(Date()): (i) received")
}

计时器触发,但未调用接收器。将分配更改为 ...

let pub = Just(33)

。解决了它。

最新更新