I think that the solution you are describing is how RxSwift (ReactiveX) solves this problem.
I believe Rx, like many other higher level abstractions would benefit from async, actors behind the scenes, as an implementation detail. בתאריך יום ד׳, 23 באוג׳ 2017 ב-20:41 מאת Joe Groff via swift-evolution <swift-evolution@swift.org>: > > On Aug 19, 2017, at 4:56 AM, Jakob Egger via swift-evolution < > swift-evolution@swift.org> wrote: > > I've read async/await proposal, and I'm thrilled by the possibilities. > Here's what I consider the canonical example: > > @IBAction func buttonDidClick(sender:AnyObject) { > beginAsync { > let image = await processImage() > imageView.image = image > } > } > > This is exactly the kind of thing I will use async/await for! > > But while this example looks extremely elegant, it would suffer from a > number of problems in practice: > > 1. There is no guarantee that you are on the main thread after `await > processImage()` > 2. There is no way to cancel processing > 3. Race Condition: If you click the button a second time before > `processImage()` is done, two copies will run simultaneously and you don't > know which image will "win". > > So I wondered: What would a more thorough example look like in practice? > How would I fix all these issues? > > After some consideration, I came up with the following minimal example > that addresses all these issues: > > class ImageProcessingTask { > var cancelled = false > func process() async -> Image? { … } > } > > var currentTask: ImageProcessingTask? > @IBAction func buttonDidClick(sender:AnyObject) { > currentTask?.cancelled = true > let task = ImageProcessingTask() > currentTask = task > beginAsync { > guard let image = await task.process() else { return } > DispatchQueue.main.async { > guard task.cancelled == false else { return } > imageView.image = image > } > } > } > > If my example isn't obvious, I've documented my thinking (and some > alternatives) in a gist: > https://gist.github.com/jakob/22c9725caac5125c1273ece93cc2e1e7 > > Anyway, this more realistic code sample doesn't look nearly as nice any > more, and I actually think this could be implemented nicer without > async/await: > > class ImageProcessingTask { > var cancelled = false > func process(completionQueue: DispatchQueue, completionHandler: > (Image?)->()) { … } > } > @IBAction func buttonDidClick(sender:AnyObject) { > currentTask?.cancelled = true > let task = ImageProcessingTask() > currentTask = task > task.process(completionQueue: DispatchQueue.main) { (image) in > guard let image = image else { return } > guard task.cancelled == false else { return } > imageView.image = image > } > } > > So I wonder: What's the point of async/await if it doesn't result in nicer > code in practice? How can we make async/await more elegant when calling > from non-async functions? > > > Yeah, it's important to understand that coroutines don't directly offer > any form of coordination; they only let you thread execution nicely through > existing coordination mechanisms. IBActions by themselves don't offer any > coordination, so anything more than fire-and-forget is still going to > require explicit code. There are some interesting approaches you still > might be able to explore to make this kind of thing nicer; for instance, if > buttonDidClick didn't directly trigger the task, but instead communicated > with a coroutine via synchronous channels in the style of Go, then that > coroutine could be responsible for filtering multiple click events, and > could also listen for cancellation events. The actor model Chris proposes > in his document could conceivably let you wrap up that low-level channel > management in a nice OO-looking wrapper. > > -Joe > > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org > https://lists.swift.org/mailman/listinfo/swift-evolution >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution