There's a thing that is still bugging me a bit, that I had mentioned it in the previous review for this proposal and was also brought up in the review for SE-0475 (Transactional Observation of Values). Basically, are there any concerns about using Task.immediate to deterministically ensure a for await loop is awaiting new values?
For example in the SE-0475 review thread this sample code was drafted:
// Assume everything has the same isolation
func startObservation() {
let values = Observations { ... }
Task.immediate {
for await value in values {
print("Fresh value", value)
}
}
}
startObservations()
// <- Is 'Fresh value' already printed, here?
doSomethingElse()
And it was empirically determined (using a dev toolchain) that the print is indeed invoked in Task.immediate before suspending (and thus before startObservation() returns).
Is this behavior reliable? Do the authors foresee any issues that could arise by using Task.immediate in this way?
I know this is more of a question about how AsyncSequences work, but if this API ends up being widely used for this, its interaction with AsyncSequences will become very relevant.
Personally I was a bit surprised that the print was invoked before Task.immediate returned, I would have expected the "real" suspension point to happen after the loop was already ready to receive values but before the closure was called for the first value.
But if I'm not mistaken that depends on the implementation of the sequence. There's nothing in AsyncIteratorProtocol that enforces anything at all happens in next() before the first "real" suspension point. So, wouldn't different conformers of AsyncSequences exhibit different behaviors when used as in the code above? If so, couldn't this API (however useful for its intended use case) become a footgun when devs inevitably try to use it to observe AsyncSequences?
The reason I find it concerning is because I can imagine the same sequence of events that we saw in the SE-0475 review thread happening to many other developers:
- Think of using this API to observe
AsyncSequences. - Test if it works as expected.
- It indeed works as expected with a test sequence.
- Discover that it fails under slightly different circumstances (different type of
AsyncSequence, different isolation...).
To be clear, I don't think there's anything wrong with the API per se, and I'd personally find it quite useful. It's just the potential for misuse that worries me.