I found a decent description about async/await here:
Asynchronous programming scenarios - C# | Microsoft Learn
So it’s much more like runloop based callbacks in Foundation that libdispatch. It’s complicated. Even the simplest example is super complicated.
It’s super implicit (like, “I’m going to package up every line of code from await to the end of this function and turn it into a continuation”). That seems to go against one of the primary principles of Swift, which is to make things plain to the reader. I’d be interested to know what the call stack looks like on the line after await.
This is pretty much how it would have to work for Swift as well. The call stack after the await (in C#) would either start at the runloop and go through the futures API (usually Task) or it would start at whatever code satisfied the async request.
It’s true that this can make it more difficult to understand stack traces. In most cases the original call stack is lost. Microsoft has made changes to Visual Studio in order to show kind of an alternative stack trace for tasks to try to make this better.
just FYI, Xcode does that too these days. If you breakpoint/crash within something that got asynchronously dispatched, you'll see a synthesised stack frame that shows you where it got enqueued from. The same could be done for async/await.
···
On 11 Sep 2017, at 10:04 pm, Adam Kemp via swift-evolution <swift-evolution@swift.org> wrote:
On Sep 11, 2017, at 1:15 PM, Kenny Leung via swift-evolution <swift-evolution@swift.org> wrote:
I think they also made things like F10 (step over) and F11 (step out) do the natural thing (i.e., wait for the continuation).
The doc takes away some of the mystery, but leaves major questions, like: await is used to yield control to the parent, but at the bottom of the call stack, presumably you’re going to do something blocking, so how do you call await?
One of the common misconceptions about async/await (which I also had when I first encountered it) is that there must be a blocking thread somewhere. It doesn’t work that way. The “bottom of the call stack” is typically either a run loop or a thread pool with a work queue (really just another kind of run loop). I guess you’re right in the sense that those kinds of run loops do block, but they’re not blocking on any particular piece of work to be done. They’re blocking waiting for ANY more work to be done (either events or items placed in the work queue).
The way that the continuation works is that it is placed onto one of those queues. For the UI thread it’s kind of like doing performSelectorOnMainThread: (the .Net equivalent is usually called BeginInvokeOnMainThread). For a thread pool there’s another API. For GCD this would be like doing a dispatch_async. It’s putting the continuation callback block onto a queue, and that callback will be called when the run loop or the thread pool is able to do so.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution