рдореИрдВ рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рдХрд┐ SignalProducer
рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛рдП рдЬреЛ рдкреВрд░реА рд╕реНрдЯреНрд░реАрдо рдХрд╛ рдЙрдкрднреЛрдЧ рд╣реЛрдиреЗ рдкрд░ рдмрд╛рд░-рдмрд╛рд░ рдбреЗрдЯрд╛ рдХрд╛ рдЕрдЧрд▓рд╛ рд╣рд┐рд╕реНрд╕рд╛ рд▓рд╛рдПрдЧрд╛ред
рдореЗрд░реЗ рдкрд╛рд╕ рдПрдХ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реИ, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдмреИрдХрдкреНрд░реЗрд╢рд░ рддрдВрддреНрд░ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ рдЕрдЧрд░ рдирд┐рд░реНрдорд╛рддрд╛ рдкрд░ observeOn
рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред рдРрд╕рд╛ рдкреНрд░рддреАрдд рд╣реЛрддрд╛ рд╣реИ рдХрд┐ рдирд┐рд░реНрдорд╛рддрд╛ рдирдпрд╛ рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЬрд╛рд░реА рд░рдЦрддрд╛ рд╣реИ, рднрд▓реЗ рд╣реА рдХреНрд▓рд╛рдЗрдВрдЯ рдиреЗ рдЕрднреА рднреА рдкреВрд░реА рд╕реНрдЯреНрд░реАрдо рдХрд╛ рдЙрдкрднреЛрдЧ рдирд╣реАрдВ рдХрд┐рдпрд╛ рд╣реИред
рдХреЛрдИ рд╡рд┐рдЪрд╛рд░ рд╣реИ рдХрд┐ рдореИрдВ рдЬреЛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдЙрд╕реЗ рдХреИрд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ? рдХреНрдпрд╛ рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╕рдВрднрд╡ рд╣реИ?
рдзрдиреНрдпрд╡рд╛рдж!
public func streamMessages(from startOffset: Offset = Offset(value: 0), toExclusive endOffsetOpt : Offset? = .None,
includeTransient: Bool = true) -> SignalProducer<Message, NoError> {
func streamMessagesChunk(from: Offset) -> SignalProducer<Message, NoError> {
func waitForNewMessageAvailable(from: Offset) -> SignalProducer<Offset?, NoError> {
return self.lastOffsetIncludingTransient(includeTransient).producer
.filter{ offsetOpt in offsetOpt.map {offset in offset >= from } ?? false }
.take(1)
}
let streamMessagesProducer = self.fetchMessages(10, from: from, includeTransientMessages: includeTransient)
.flatMap(.Concat){ messages in SignalProducer<Message, NoError>(values: messages)}
return waitForNewMessageAvailable(from)
.then(streamMessagesProducer)
}
func streamNextBatch(from: Offset, observer: Observer<Message, NoError>, observerDisposable: CompositeDisposable) -> Void {
func hasReachedEndOffset(currentOffset: Offset) -> Bool {
return endOffsetOpt.map{ endOffset in endOffset == currentOffset } ?? false
}
print("StreamNextBatch \(from)")
streamMessagesChunk(from).startWithSignal { signal, signalDisposable in
var lastOffset: Offset = from
let disposableHandle = observerDisposable.addDisposable(signalDisposable)
signal.observe { switch $0 {
case let .Failed(error): observer.sendFailed(error)
case .Interrupted: observer.sendInterrupted()
case .Completed:
disposableHandle.remove()
streamNextBatch(lastOffset.next, observer: observer, observerDisposable: observerDisposable)
case .Next(let message):
if hasReachedEndOffset(message.offset) {
disposableHandle.remove()
observer.sendCompleted()
} else {
lastOffset = message.offset
observer.sendNext(message)
}
}
}
}
}
return SignalProducer<Message, NoError> { observer, observerDisposable in
streamNextBatch(startOffset, observer: observer, observerDisposable: observerDisposable)
}
}
func testShouldStreamMessagesWaitingForFutureMessages() {
let expectation = self.expectationWithDescription("Test")
let messages = (0...50000).map{value in self.createKafkaData(UInt64(value)) }
let nextMessages = (50001...65000).map{value in self.createKafkaData(UInt64(value)) }
try! self.sut.publishMessages(messages, persist: false).get()
let messageCountFuture = self.sut
.streamMessages(from: Offset(value: 45), toExclusive: Offset(value: 60000), includeTransient: true)
.observeOn(QueueScheduler())
.map{ m in print("sleeping at \(m.data)"); sleep(1); return 1 }
.reduce(0, +)
.toFuture()
messageCountFuture.onSuccess{ count in
expect(count) == 15
expectation.fulfill()
}
try! self.sut.publishMessages(nextMessages, persist: false).get()
self.waitForExpectationsWithTimeout(30, handler: nil)
}
func createKafkaData(number: UInt64) -> String {
return "message \(number)"
}
_рдПрдХ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рд▓рдВрдмреЗ рд╡рд┐рд▓рдВрдм рдХреЗ рд▓рд┐рдП рдХреНрд╖рдорд╛ рдХрд░реЗрдВред рд╣рдо рдХрдИ рдкреБрд░рд╛рдиреЗ рдореБрджреНрджреЛрдВ рдкрд░ рдХрд╛рдо рдХрд░ рд░рд╣реЗ рд╣реИрдВ рдХреНрдпреЛрдВрдХрд┐ рдХреБрдЫ рдпреЛрдЧрджрд╛рдирдХрд░реНрддрд╛рдУрдВ рдиреЗ рдЫреЛрдбрд╝ рджрд┐рдпрд╛ рд╣реИред
рдЕрдлрд╕реЛрд╕ рдХреА рдмрд╛рдд рд╣реИ, AFAIK рд╡рд░реНрддрдорд╛рди рдореЗрдВ рд╢реЗрдбреНрдпреВрд▓рд░реНрд╕ рдореЗрдВ рдмреИрдХрдкреНрд░реЗрд╢рд░ рдкреНрд░рджрд╛рди рдХрд░рдиреЗ рдХрд╛ рдХреЛрдИ рддрд░реАрдХрд╛ рдирд╣реАрдВ рд╣реИред ЁЯШЮ
рдХреНрдпрд╛ рдЖрдк рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рдХреНрдпрд╛ рдХреЛрдИ рдЕрдиреНрдп рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рд╢реАрд▓ рдврд╛рдВрдЪрд╛ рдРрд╕рд╛ рдХрд░рдиреЗ рдХрд╛ рдХреЛрдИ рддрд░реАрдХрд╛ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ? рдореБрдЭреЗ рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рджрд┐рд▓рдЪрд╕реНрдк рдЕрд╡рдзрд╛рд░рдгрд╛ рд╣реИред
рдЬрд╣рд╛рдВ рддрдХ тАЛтАЛрдореБрдЭреЗ рдкрддрд╛ рд╣реИ, рдЖрд░рдПрдХреНрд╕ рдкрд░рд┐рд╡рд╛рд░ рдХреЗ рдврд╛рдВрдЪреЗ рдЖрдо рддреМрд░ рдкрд░ рдмреИрдХрдкреНрд░реЗрд╢рд░ рдХрд╛ рд╕рдорд░реНрдерди рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдзрд╛рд░рд╛рдУрдВ рдХрд╛ рдПрдХ рд╡рд┐рд╢реЗрд╖ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди (рдЕрдХреНрдХрд╛-рд╕реНрдЯреНрд░реАрдо) рдЗрд╕рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ: http://www.smartjava.org/content/visualizing-back- рджрдмрд╛рд╡
рд╡рд┐рдЪрд╛рд░ рдпрд╣ рд╣реИ рдХрд┐ рдбреЗрдЯрд╛ рдиреАрдЪреЗ рдХреА рдУрд░ рдкреНрд░рд╡рд╛рд╣рд┐рдд рд╣реЛрддрд╛ рд╣реИ рдФрд░ рдорд╛рдВрдЧ рдКрдкрд░ рдХреА рдУрд░ рдкреНрд░рд╡рд╛рд╣рд┐рдд рд╣реЛрддреА рд╣реИ, рдЗрд╕рд▓рд┐рдП рдкреНрд░рд╛рдкреНрддрдХрд░реНрддрд╛ рд╣рдореЗрд╢рд╛ рдЕрдзрд┐рдХрддрдо рдЖрдиреЗ рд╡рд╛рд▓реА рдбреЗрдЯрд╛ рджрд░ рдХреЗ рдирд┐рдпрдВрддреНрд░рдг рдореЗрдВ рд░рд╣рддрд╛ рд╣реИред
рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛ RxSwift рдореЗрдВ рдмреИрдХрдкреНрд░реЗрд╢рд░ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЙрдирдХрд╛ рдбрд┐рдлрд░ рдСрдкрд░реЗрдЯрд░ рд╕рдорд╛рди рдЬрд░реВрд░рддреЛрдВ рдХреЛ рдкреВрд░рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рд╣реЛрдЧрд╛: http://reactivex.io/documentation/operators/defer.html
ReactiveCocoa рдореЗрдВ рдРрд╕рд╛ рд╕реНрдердЧрд┐рдд рдСрдкрд░реЗрдЯрд░ рд╣реЛрдирд╛ рдХрд┐рддрдирд╛ рдХрдард┐рди рд╣реЛрдЧрд╛?
рдореБрд╢реНрдХрд┐рд▓ рдирд╣реАрдВред рдореИрдВрдиреЗ рд░реЗрдХреНрд╕ рдореЗрдВ рдХреБрдЫ рдРрд╕рд╛ рд╣реА рдХрд┐рдпрд╛ред рд╣рд╛рд▓рд╛рдВрдХрд┐, рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЖрдк рдПрдХ рд╕рд╛рдорд╛рдиреНрдпреАрдХреГрдд рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд▓рд┐рдП рдкреВрдЫ рд░рд╣реЗ рд╣реИрдВред рджреЗрд░реА рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рдордп рдЕрдВрддрд░рд╛рд▓ рдХреЗ рдмрдЬрд╛рдп рдЖрдк рд╕рджрд╕реНрдпрддрд╛ рдХреЛ рд╕реНрдердЧрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЯреНрд░рд┐рдЧрд░ рд╕рд┐рдЧреНрдирд▓/рдирд┐рд░реНрдорд╛рддрд╛ рдкрд░рдо рдЪрд╛рд╣рддреЗ рд╣реИрдВред
@neilpa рд╣рдореЗрдВ рддрдм рддрдХ рд╕рджрд╕реНрдпрддрд╛ рдХреЛ рд╕реНрдердЧрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА рдЬрдм рддрдХ рдХрд┐ рдЙрдкрднреЛрдХреНрддрд╛ рдиреЗ рд╕реНрдЯреНрд░реАрдо рдХрд╛ рдЙрдкрднреЛрдЧ рдирд╣реАрдВ рдХрд░ рд▓рд┐рдпрд╛ рд╣реЛред рдпрд╣ ReactiveCocoa
(рдКрдкрд░ рдЙрджрд╛рд╣рд░рдг) рдХреЗ рд╕рд╛рде рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ рдЬрдм рддрдХ рдХрд┐ рдЖрдк рдПрдХ рдЕрд▓рдЧ рд╢реЗрдбреНрдпреВрд▓рд░ рдкрд░ рд╕рджрд╕реНрдпрддрд╛ рд▓реЗрдирд╛ рд╢реБрд░реВ рдирд╣реАрдВ рдХрд░рддреЗред
рдореИрдВ рдЗрд╕реЗ рдмрдВрдж рдХрд░рдиреЗ рдЬрд╛ рд░рд╣рд╛ рд╣реВрдВ рдХреНрдпреЛрдВрдХрд┐ рдЖрдкрдХреЗ рдкреНрд░рд╢реНрди рдХрд╛ рдЙрддреНрддрд░ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдпрджрд┐ рдЖрдк рдХрд┐рд╕реА рдЪреАрдЬрд╝ рдХрд╛ рдкреНрд░рд╕реНрддрд╛рд╡ рджреЗрдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рдмреЗрдЭрд┐рдЭрдХ рдПрдХ рдирдпрд╛ рдЕрдВрдХ рдпрд╛ рдкреАрдЖрд░ рдЦреЛрд▓реЗрдВ!