As discussed previously on this forum, the order of execution for tasks on actors is not deterministic. Unlike serial queues, actors are not strictly first-in, first-out.
In WWDC22 - Eliminate data races using Swift Concurrency, this is touched upon around the 23:20 mark and the importance of this is called out.
Two solutions are proposed by the presenter to allow for task ordering:
- Use a single
Task
because they execute from beginning to end. - Use an
AsyncStream
There are many use-cases where the first option is not viable. The second option seems promising as the presenter notes:
AsyncStream can be used to model an actual stream of events.
One task can iterate over the stream of events with a for-await-in loop, processing each event in turn.
An AsyncStream can be shared with any number of event producers, which can add elements to the stream while maintaining order.
Is there any sample code or example of this technique that can be shared to help some of us better understand how to implement that pattern properly?
Consider the use-case of a Database
that is modelled as an actor
:
actor Database {
func insert() {}
func update() {}
func delete() {}
}
In such an implementation, it's critical that the order of tasks be deterministic. If a user were to create a record and then immediately update a record, the Database
needs to process the insert()
"task" first followed by the update()
"task".
How can one use AsyncStream
to model such a use-case as suggested by the session video?