The way I have solved that is with a serial queue. You then use an AsyncOperation class to hold the contents of the func, and submit that to the queue. Something like this
func downloadAndStore(completion: @escaping () -> Void) {
serialQueue.addOperation(
AsyncOperation { finishHandler in
loadWebResource("data.txt") { download in
database.store(download) {
completion()
finishHandler()
}
}
}
)
}
There is no AsyncOperation in Cocoa, but the Operation class supports the concept of an async operation, so it is quite easy to make a subclass (eg LLVS/AsynchronousOperation.swift at master · mentalfaculty/LLVS · GitHub)
How would this look in async/await? I suspect you can do this with AsyncStream (How do you use AsyncStream to make Task execution deterministic?), but it is not really any more elegant than the AsyncOperation approach above, and not that accessible to inexperienced developers.
For this reason, I think it would be nice if actors could take care of the streams, queues or whatever they need to make this happen, so the developer can just write something like this
actor DataManager {
func downloadAndStore() atomic {
let download = await loadWebResource("data.txt")
await database.store(download)
}
}