flatMapLatest + Just issues
I don't know if I'm writing an issue or a feature request or just question. I have the following scenario, very simplified:
let root = CurrentValue(false)
let results = root.flatMapLatest {
$0 ? Just(...).eraseToAnyAsyncSequence() : AsyncStream { ... }.eraseToAnyAsyncSequence()
}
// later...
for await result in results { ... }
fatalError()
I put the fatalError after the loop because as I see it, root never completes and so we'd never reach that point. However, whenever the condition becomes true and we use Just, collection of results also exits. Now I have a few questions:
-
Is this expected? Shouldn't flatMapLatest continue even after some inner children completes, given that the outer sequence has not completed?
-
Is there anything else in the library that would help me? I tried using
CurrentValue(...)instead of Just, but in that case I get no results. I think it has to do with CurrentValue being garbage collected, if I store it in a local variable it works. I'm not 100% clear on the mechanics that lead to this though (shouldn't eraseToAnySequence keep a ref to the class?) -
Would it make sense to add something like
Just(value, completeImmediately: Bool)for this use case? In the spirit of https://developer.apple.com/documentation/combine/empty/init(completeimmediately:) -
Side note, https://github.com/sideeffect-io/AsyncExtensions/blob/main/Sources/Operators/AsyncSequence%2BEraseToAnyAsyncSequence.swift#L28 wraps the closure twice, should just invoke it I think
Hi @natario1
Thanks for this issue.
I'm in the process of refactor this lib to make it a great companion to the Apple repo swift-async-algorithms.
I have a branch refactor where I've re-implemented a lot of things (still in progress). Perhaps the behaviour you are describing does not happen anymore on this branch.
Thanks @twittemb ! I tried refactor, looks like the problem is still there. It's even a bit worse maybe, the workaround I use in 0.4.0 does not work in refactor
I think there might be an issue with my implementation of AsyncSwiftToLatestSequence where I end the sequence when the child async sequence finishes.
I'll write it in my to do list :-( (or you can try to fix it if you want :-) )
Thanks! In case I give it a try, should I work on refactor ?
yep
Hi @natario1
I've just made a quick test on the refactor branch and I think the result is OK (I haven't changed anything yet)
let exp = expectation(description: "")
let subject = AsyncPassthroughSubject<Bool>()
let sut = subject.flatMapLatest { $0 ? AsyncJustSequence<Int>(1) : AsyncJustSequence<Int>(2) }
Task {
for await element in sut {
print(element)
}
exp.fulfill()
}
try? await Task.sleep(nanoseconds: 1_000_000_000)
subject.send(false)
try? await Task.sleep(nanoseconds: 1_000_000_000)
subject.send(true)
try? await Task.sleep(nanoseconds: 1_000_000_000)
subject.send(false)
wait(for: [exp], timeout: 10)
It prints: 2 1 2
and never finishes.
Nice, I'll try again with the new release.
You can try the 0.5.0 if you want.
I tried to pull 0.5.0 but SPM complains about the unsafe flags in package definition. Is this something you forgot to remove?
the target 'AsyncExtensions' in product 'AsyncExtensions' contains unsafe build flags.
It was the first time I tried those flags. This idea was to ensure my code was fully "swift concurrency" proof. I did not know it would generate warning for the users.
Did you manage to use the package anyway ?
Well no, it's not a warning, it fails to sync. I read somewhere that dependencies with unsafe flags are only allowed on local packages and branches (like refactor), but not tags.
ok let me fix this in a 0.5.1
You can give it a try :-)