Asynchronous Programming in C# 5.0 part two: Whence await?
I want to start by being absolutely positively clear about two things, because our usability research has shown this to be confusing. Remember our little program from last time?
async void ArchiveDocuments(List<Url> urls)
{
Task archive = null;
for(int i = 0; i < urls.Count; ++i)
{
var document = await FetchAsync(urls[i]);
if (archive != null)
await archive;
archive = ArchiveAsync(document);
}
}
The two things are:
1) The “async” modifier on the method does not mean “this method is automatically scheduled to run on a worker thread asynchronously”. It means the opposite of that; it means “this method contains control flow that involves awaiting asynchronous operations and will therefore be rewritten by the compiler into continuation passing style to ensure that the asynchronous operations can resume this method at the right spot.” The whole point of async methods it that you stay on the current thread as much as possible. They’re like coroutines: async methods bring single-threaded cooperative multitasking to C#. (At a later date I’ll discuss the reasons behind requiring the async modifier rather than inferring it.)
2) The “await” operator used twice in that method does not mean “this method now blocks the current thread until the asynchronous operation returns”. That would be making the asynchronous operation back into a synchronous operation, which is precisely what we are attempting to avoid. Rather, it means the opposite of that; it means “if the task we are awaiting has not yet completed then sign up the rest of this method as the continuation of that task, and then return to your caller immediately; the task will invoke the continuation when it completes. ”
It is unfortunate that people’s intuition upon first exposure regarding what the “async” and “await” contextual keywords mean is frequently the opposite of their actual meanings. Many attempts to come up with better keywords failed to find anything better. If you have ideas for a keyword or combination of keywords that is short, snappy, and gets across the correct ideas, I am happy to hear them. Some ideas that we already had and rejected for various reasons were:
wait for FetchAsync(…)
yield with FetchAsync(…)
yield FetchAsync(…)
while away the time FetchAsync(…)
hearken unto FetchAsync(…)
for sooth Romeo wherefore art thou FetchAsync(…)
Moving on. We’ve got a lot of ground to cover. The next thing I want to talk about is “what exactly are those ‘thingies’ that I handwaved about last time?”
Last time I implied that the C# 5.0 expression
document = await FetchAsync(urls[i])
gets realized as:
state = State.AfterFetch;
fetchThingy = FetchAsync(urls[i]);
if (fetchThingy.SetContinuation(archiveDocuments))
return;
AfterFetch: ;
document = fetchThingy.GetValue();
what’s the thingy?
In our model for asynchrony an asynchronous method typically returns a Task<T>; let’s assume for now that FetchAsync returns a Task<Document>. (Again, I’ll discuss the reasons behind this "Task-based Asynchrony Pattern" at a later date.) The actual code will be realized as:
fetchAwaiter = FetchAsync(urls[i]).GetAwaiter();
state = State.AfterFetch;
if (fetchAwaiter.BeginAwait(archiveDocuments))
return;
AfterFetch: ;
document = fetchAwaiter.EndAwait();
The call to FetchAsync creates and returns a Task<Document> - that is, an object which represents a “hot” running task. Calling this method immediately returns a Task<Document> which is then somehow asynchronously fetches the desired document. Perhaps it runs on another thread, or perhaps it posts itself to some Windows message queue on this thread that some message loop is polling for information about work that needs to be done in idle time, or whatever. That’s its business. What we know is that we need something to happen when it completes. (Again, I’ll discuss single-threaded asynchrony at a later date.)
To make something happen when it completes, we ask the task for an Awaiter, which exposes two methods. BeginAwait signs up a continuation for this task; when the task completes, a miracle happens: somehow the continuation gets called. (Again, how exactly this is orchestrated is a subject for another day.) If BeginAwait returns true then the continuation will be called; if not, then that’s because the task has already completed and there is no need to use the continuation mechanism.
EndAwait extracts the result that was the result of the completed task.
We will provide implementations of BeginAwait and EndAwait on Task (for tasks that are logically void returning) and Task<T> (for tasks that return a value). But what about asynchronous methods that do not return a Task or Task<T> object? Here we’re going to use the same strategy we used for LINQ. In LINQ if you say
from c in customers where c.City == "London" blah blah blah
then that gets translated into
customers.Where(c=>c.City=="London") …
and overload resolution tries to find the best possible Where method by checking to see if customers implements such a method, or, if not, by going to extension methods. The GetAwaiter / BeginAwait / EndAwait pattern will be the same; we’ll just do overload resolution on the transformed expression and see what it comes up with. If we need to go to extension methods, we will.
Finally: why "Task"?
The insight here is that asynchrony does not require parallelism, but parallelism does require asynchrony, and many of the tools useful for parallelism can be used just as easily for non-parallel asynchrony. There is no inherent parallelism in Task; that the Task Parallel Library uses a task-based pattern to represent units of pending work that can be parallelized does not require multithreading.
As I've pointed out a few times, from the point of view of the code that is waiting for a result it really doesn't matter whether that result is being computed in idle time on this thread, in a worker thread in this process, in another process on this machine, on a storage device, or on a machine halfway around the world. What matters is that it's going to take time to compute the result, and this CPU could be doing something else while it is waiting, if only we let it.
The Task class from the TPL already has a lot of investment in it; it's got a cancellation mechanism and other useful features. Rather than invent some new thing, like some new "IFuture" type, we can just extend the existing task-based code to meet our asynchrony needs.
Next time: How to further compose asynchronous tasks.
Comments
Anonymous
October 28, 2010
If I haven't gotten hopelessly confused, it would seem that the behavior of "await" has some similarities with "yield return". If that's the case, perhaps revisiting various proposals involving "yield" would be appropriate.Anonymous
October 28, 2010
It amused me to no end to see that the switch from Begin/End async style (already almost perfect for manual CPS) to the new event-based async style (that made manual CPS very painful, and that I personally detested) has been reversed with much ugly code in the CTP, just to get back to something along the lines of Begin/End again. Take a look at how awkwardly the WebClient.DownloadStringTaskAsync extension method was implemented, compared to Stream.ReadAsync and Stream.WriteAsync.Anonymous
October 28, 2010
"yield for" has the right meaning, I think, although both words have a different sense here than the existing keywords.Anonymous
October 28, 2010
The comment has been removedAnonymous
October 28, 2010
What about ohwait, or the odata inspired owait... var MyData = ohwait GetMyData(....);Anonymous
October 28, 2010
I'am very curious why yield AsyncFetch(…) was rejected for “await” syntax.Anonymous
October 28, 2010
The comment has been removedAnonymous
October 28, 2010
The comment has been removedAnonymous
October 28, 2010
... or to combine my suggestion and StarBright's, how about "continue after"? async void ArchiveDocuments(List<Url> urls) { Task archive = null; for(int i = 0; i < urls.Count; ++i) { continue after (var document = FetchAsync(urls[i])); if (archive != null) continue after archive; archive = ArchiveAsync(document); } }Anonymous
October 28, 2010
Looks like it should be AsynchAppend, which would show he control flow... which really is the purpose...Right? Your Description::“if the task we are awaiting has not yet completed then sign up the rest of this method as the continuation of that task, and then return to your caller immediately; the task will invoke the continuation when it completes.” so really what your doing is appending this method onto the previous task, asynchronously, which would lead me to believe that you should have the ability to do it in a blocking manor (the pair of which may be useful in some situations) SO, I think AAppend or AsynchAppend, AppendA etc.. and the blocking counterpart BlockAppend. unless the jiter can tell the difference from context..?, or we can attribute the method as [asynch]/[blocking], with asynch as the default... (I feel like a child on Santa's lap right now...)Anonymous
October 28, 2010
Your usability research is right. It is confusing. I find it very difficult to read your code snippet. To be honest it is not clear what's happening. I don't think the problem has to do with the naming of the keywords. It's too much magic going on. On the other hand i'm not the smartest person on the planet. But should i? I just want to read it...Anonymous
October 28, 2010
The comment has been removedAnonymous
October 29, 2010
Or how about "yield until"? "yield until archive;" I still like "continue after" best, though. There's all sorts of ways you could go with syntaxes for the assigning version, too. "continue after AsyncFetch(...) into document;" "continue after document is AsyncFetch(...)" "continue after document from AsyncFetch(...)"Anonymous
October 29, 2010
How about "yield continue"? As with enumerators, yield says "I'm temporarily giving up control"; continue is already a keyword that happens to look similar to the behind-the-scenes mechanism.Anonymous
October 29, 2010
Just so this voice is heard: async and await are fine to me. Of course not all subtleties of these new keywords have sunk in, but they will, and it seems to me "async" and "await" will feel quite natural after using them a bit. I don't see why "after", "continue", "..." should feel better. btw, yield is a tough word for non-native english speakers: When I look it up in the dictionary it has a gazillion meanings.Anonymous
October 29, 2010
I think that "resume after" better captures the intent than "await", although "yield until" has a nice ring to it.Anonymous
October 29, 2010
I also find it very difficult to read that code snippet. Let me see if I have this right: The function calls FetchAsync on each element of urls, and arranges for ArchiveAsync to be called on each result whenever that appears. No matter how many times I try to read the code snippet, I can't convince myself that that's what it says. I encourage you to take the feedback seriously: "await" sounds like it does the opposite of what it actually does. It's like when someone breaks up with you and says "See you around." I think you're having problems with people understanding "async" because the word "asynchronous" is unfortunate in the first place. It doesn't really mean what it sounds like, because "synchronous" in this context means not "at the same time" but "sequentially." So what we call "asynchronous" in CS should really have been called "non-sequential." Anyway, it's a highly technical meaning and it doesn't deserve to be used outside of CS papers. I realize you have probably spent many hours on this, so what do I know. But since Every Programmer Has An Opinion (tm), here's my suggestion: someday void ArchiveDocuments(List<Url> urls) { Task archive = null; for(int i = 0; i < urls.Count; ++i) { when(var document = FetchAsync(urls[i])) { when(archive == null) archive = ArchiveAsync(document); } } } Here, "someday" clearly indicates that the function is to schedule doing something eventually, rather than actually doing those things now, but does not have the same thread-related baggage as "async." The "when" block clearly indicates the contents of the continuation, and it does not suggest (which the await syntax does) that the contents of the block will be executed before the when clause finishes. It also resembles using blocks, because it's a block that sets up a context in which a given variable is defined. The only difference is that unlike a using, it does not block on the clause finishing, but continues the program, and executes the block whenever the clause is done.Anonymous
October 29, 2010
This: after (var document = FetchAsync(urls[i])): .. would make no sense. FetchAsync is returning a Task<T>. await is an operation on that task. It's not that we are waiting (sync or async) on the assignment to take place. I think the lexical syntax is completely understandable, however, 'yield', 'yield for', or 'yield until' may have made clearer syntax. I like to think of 'await' as meaning 'asynchronous wait'. It's the mathematical dual of 'yield'! (ok, maybe not)Anonymous
October 29, 2010
I agree with @M.E. that the "async" keyword could also be improved on, but it seems to me it rather depends on what you do with "await". "yielding" or "continuing" would be possible options, for example. I wonder whether you also considered requiring a keyword on calls to async methods even when they are not being immediately awaited? For example, ArchiveAsync returns void. So "archive = ArchiveAsync(document)" seems weird. How about "archive = begin ArchiveAsync(document);"? That way any call to an async method that didn't have either "begin" or "await" (or await's replacement) would be in error - I suspect that would avoid some misunderstandings or confusing code.Anonymous
October 29, 2010
@Jason: "FetchAsync is returning a Task<T>." I thought it was declared as returning "async Document"?Anonymous
October 29, 2010
I think 'async' and 'await' are fine. People are going to have to clear up their misconceptions about asynchronous programming and threads - it's not like Stream.BeginWrite/EndWrite were ever creating threads. That said, I do like the idea of using 'yield' in there, as that reminds me of cooperative multitasking. Maybe something like "yield wait"? @Stuart: you can't do that, there already are plenty of asynchronous methods in existing code. Remember that anything returning Task (or something else with a GetAwaiter() method) can be used with "await" - there's no need for an "async" modifier on the method, and callers of the method really shouldn't care how it was implemented - whether the compiler generated the task (using "async" keyword) or whether the programmer wrote the task-creating code manually (without using "async" keyword).Anonymous
October 29, 2010
I doff my cap to the folks working on this model of async programming. I'm very impressed with how easy it is to interpret and understand this model and how well it composes with itself (which is typically one of the challenges of asynchronous development). Two things, however, I'm very curious to see are the debugging experience and the BCL enhancements relating to this model of asynchrony. Intuitive debugging in an async environment is hard to achieve - and with composable continuations, I suspect that we'll need some new debugging capabilities to make sure developers can wrap their heads around what is happening in the code ... especially with the amount of sophisticated compiler magic involved.Anonymous
October 29, 2010
I love "hearken unto". I think it fits great.Anonymous
October 29, 2010
Why does Task<T> contain the method GetValue() when a property seems more appropriate? (Regardless of what it compiles to.) Or are there parameter versions of it as well?Anonymous
October 29, 2010
I think the choice of keyword possibly depends on what point you're trying to get across: what happens at the start of the operation ("yield back to the caller") or what happens at the end of the operation ("continue with the rest of the program flow"). One subtelty which hasn't been brought up - at least in the comments - is that it might not yield back to the caller. If the call to awaiter.BeginAwait() returns false (IIRC - maybe true; I haven't got the spec in front of me) then the method will continue immediately. Likewise although Eric mentioned continuing "on the same thread" that's only necessary for some synchronization contexts - such as ones in a UI. If you're writing server-side code and this is executing on a thread-pool thread, I believe the default synchronization context will allow it to continue on any random thread pool thread later. I think "async" is fine, but I think I'd like something involving "continue" where we've currently got "await". For example: var result = continue after DownloadStringTaskAsync(url);Anonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
Declaring 'async Document' as the return type will compile as a method returning Task<Document>.Anonymous
October 29, 2010
@Stuart, declaring 'async Document' as the return type will compile as a method returning Task<Document>.Anonymous
October 29, 2010
I think that a new concept deserves a new keyword. Granted, that technically, "async" is not currently a keyword, but I think that if it was used as shown, then a general misunderstanding would be present. Putting something like "async" on a method declaration, at first inspection, I would think that it executes on a different thread. Consider using a different word: How about "deferred"? Not sure about "await" I agree it feels somewhere between "yield" and "continue". Just throwing things together: How about "continue when"? deferred void ArchiveDocuments(List<Url> urls) { Task archive = null; for(int i = 0; i < urls.Count; ++i) { var document = continue when FetchAsync(urls[i]); if (archive != null) continue when archive; archive = ArchiveAsync(document); } }Anonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
@LBushkin: Yes, it shouldn't be a problem. You write a GetAwaiter() extension method for IAsyncResult, which returns something with BeginAwait(Action) and EndAwait(). That's the beauty of it being a pattern rather than a specific type. The only ties to Task that I'm aware of are that an async method/anonymous function will always return a Task or Task<T>. I don't believe the way that that task is constructed is specified.Anonymous
October 29, 2010
@Juozas, async doesn't imply threading. It could be async kernel IO, Windows messages, etc, that are providing the asynchrony.Anonymous
October 29, 2010
@Juozas: Using "threaded" would definitely be the wrong word, IMO. It means there's a natural assumption that extra threads are involved, when they may well not be. For example, when you issue an asynchronous IO request, that doesn't require an extra thread to be running. You can have many, many asynchronous requests in flight at a time, using IO completion ports but not extra threads and the stack space hit incurred by those threads.Anonymous
October 29, 2010
Does a simple 'defer' work? var document = defer FetchAsync(urls[i]); "defer" is sort like saying "I'll take care of this later"...Anonymous
October 29, 2010
I haven't had a chance to play with the CTP yet, but it occurs to me that one construct that may be complicated by CPS-based asynchrony is using( ). How does something like this translate into a state machine: using( var conn = AcquireConnection() ) { await SomeAsyncWork( conn ); } What happens to deterministic disposal with exception occur, or cancellation is performed?Anonymous
October 29, 2010
I find myself turning the await statement into something like a using block which I find much clearer. I think the problem I have is not the keyword but seeing the different code blocks that will be executed. This seems much less upside down and inside out. async void ArchiveDocuments(List<Url> urls) { Task archive = null; for(int i = 0; i < urls.Count; ++i) { await (var document = FetchAsync(urls[i]) { await(archive == null || archive) { archive = ArchiveAsync(document); } } } }Anonymous
October 29, 2010
async void ArchiveDocuments(List<Url> urls) { Task archive = null; for(int i = 0; i < urls.Count; ++i) { var document = upon FetchAsync(urls[i]); if (archive != null) upon archive; archive = ArchiveAsync(document); } } Thoughts?Anonymous
October 29, 2010
As others have noted, the connection with yield might be helpful, so how about: yield resume. This keyword combo this indicates that it behaves like enumerators by yielding immediately, and the "resume" implies a resumption from that point.Anonymous
October 29, 2010
The obvious choice would be to split await between let!, do! & using!. Though, snark aside, it just struck me - I've not seen anything covering the "using!" scenarios yet.Anonymous
October 29, 2010
"hearken unto" is definitely the best one so far. That would bring C# to the pinnacle of awesomeness.Anonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
I want to put in my vote for the "continue after" syntax. When I'm writing async code I tend to think of the control flow as a queue of operations (even though, strictly speaking, it's more like a pipeline). The phrase "continue after" unambiguously says "schedule the rest of this stuff after X in the queue". It's far better than both "yield" and "await" in the sense that those two statements imply, well, waiting, which we might not actually be doing. The phrase "continue after" doesn't, necessarily imply a wait; it should be intuitive that if the thing before us in the queue is already finished, then we're going to plow on ahead.Anonymous
October 29, 2010
Cool stuff!! What about one of these? yield completed AsyncFetch(…) yield future AsyncFetch(…) future AsyncFetch(…) yield defer AsyncFetch(…) defer AsyncFetch(…)Anonymous
October 29, 2010
How about stop; FetchAsync(...) time;Anonymous
October 29, 2010
How about: var document = GOSUB FetchAsync(urls[i]); Yeah, I have terrible ideas. I'll see myself out...Anonymous
October 29, 2010
Eric, I have a bit of criticism. When you want to clarify a term you should not do it with a sentence as long and complex as: it means “this method contains control flow that involves awaiting asynchronous operations and will therefore be rewritten by the compiler into continuation passing style to ensure that the asynchronous operations can resume this method at the right spot.” Even at the cost of some accuracy. Prefer shorter bite size sentences. I am now much, much more confused than I was. Also you should not explain this from the POV of the compiler (which is what you are used to) but from the POV of a user. understanding how a feature is compiled is not Introductory by any means, again, even if it accurately depicts the behavior. I know explaining things you work with daily from start is difficult, I deal with that myself and make such errors all the time. This is why I wrote you this criticism. BTW, Thanks for the blog, read regularly.Anonymous
October 29, 2010
It just occurred to me that this type of control flow is actually reminiscent of INTERCAL: en.wikipedia.org/.../COMEFROM Maybe instead of "await", the syntax should be "come from"!Anonymous
October 29, 2010
I'm glad Eric posted on this today, my brain kept me up most of the night trying to work out whether await was short for async-wait, and how waiting could be synonymous with NOT waiting. That and pondering whether/why the async keyword was even necessary. Given that I'm not clear about the meaning/necessity of async, I can't really argue with it, but here are a few more suggestions to add to the list: yield while (my favourite, and look!, no new keywords) pending on done resume next (wink at vb6) Shame we can't vote here.. if you feel strongly enough about it, try <a href="programmers.stackexchange.com/.../a>.Anonymous
October 29, 2010
Just saw this question on Don Syme's blog, thought this might be a good place to ask it too: "Does this mean C# and VB will now also be relying upon tail call elimination in the VM?"Anonymous
October 29, 2010
Eric, I hope you're still reading these comments. I'd like to suggest: var document = yield while FetchAsync(urls[i]);yield while
perfectly captures the semantic meaning of the code -- it yields while FetchAsync runs, and (just like the similar iterator construct) regains control once the value is processed.yield until
would work also, but including the wordyield
, in my opinion, is a must -- the similarity to iterators is too great not to use the same keyword for both. To quote John Leidegren from Stack Overflow, you are yielding control, rather than yielding values. (And to top it off, it reuses existing keywords) Just like in your usability tests, theawait
keyword was a major barrier to me understanding the concept. I'm very curious whyyield ____ task;
and its variants were rejected, if you'd be so kind as to respond with a sentence or two.Anonymous
October 29, 2010
The "async" modifier confused me more than the await operator. Per your quote in the article: " They’re like coroutines: async methods bring single-threaded cooperative multitasking to C#." How about "comethod" instead of "async" for the modifier.Anonymous
October 29, 2010
@John. "yield while" gets my vote.Anonymous
October 29, 2010
I perfer to think of "await" as "when the tasks completes" so to me the keyword "when" makes more sense than await. It's also one less letter to type! Task<string> data = DownloadAsync(url); string str = when data; How about renaming await to when?Anonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
I'll second the vote for "deferred" instead of "async". As for "await", "yield until" or (second choice) "yield while" seem better.Anonymous
October 29, 2010
@John I was just about to offer the exact same suggestion. It captures the intent of the operation rather than its implementation. +1 yield whileAnonymous
October 29, 2010
Why not just make it "async DoWork(...)", since you're basically saying "do this work asynchronously"Anonymous
October 29, 2010
I like 'continue after' or 'defer until' in place of 'await'. They give the needed feel that the program is going to go off and do something else and then come back here when this step completes. The jury is still out on async. I keep flip-flopping on whether it makes sense in my gut. Regardless of the symantics, though, a beautiful language feature to have.Anonymous
October 29, 2010
+1 yield whileAnonymous
October 29, 2010
I vote for "yield for" or "yield while".Anonymous
October 29, 2010
While we're throwing mad ideas around, how about "return until"? After all, that's precisely what it actually does in practice.Anonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
Considerably, the article is in reality the greatest on this noteworthy topic. I agree with your conclusions and will eagerly look forward to your next updates. Saying thanks will not just be sufficient, for the wonderful clarity in your writing. I will immediately grab your rss feed to stay privy of any updates. Pleasant work and much success in your business dealings!Anonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
I'd pick 'task' as the keyword for the method modifier. That's what it makes. But 'await' is fine for the 2nd keyword. (Though I could live with 'hence'.)Anonymous
October 29, 2010
Comments don't really let me go into enough details here, so I've blogged about it: msmvps.com/.../c-5-async-and-choosing-terminology-why-i-m-against-quot-yield-quot.aspxAnonymous
October 29, 2010
Hi there, I am also completely confused by the await keyword. It feels like the code is lying, because "await" has the described "blocking, synchronous" feel to it. The yield option doesn't feel right either. What do you think of the following? async void ArchiveDocuments(List<Url> urls) { Task archive = null; for(int i = 0; i < urls.Count; ++i) { var document = will FetchAsync(urls[i]); if (archive != null) will archive; archive = ArchiveAsync(document); } } Making a literal transition of the snippet, I was trying to come up with some thing that would not have the same artificial feel of what is going on behind the scenes. The "await" keyword needs a lot of explanation, and so does every combination of yield. Ok, sure, my suggestion needs some explanation here, too: The simple WILL keyword is an indicator that there is something going on somewhere, but not right now. Synchronus code is more like: HEY, DO IT RIGHT NOW OR I, MYSELF WILL STOP DOING ANYTHING ELSE. Using "will" is more like a child saying to his parents: Yeah, I WILL clean up my room some time this week, but not right now, Pokemon is on. The parents are good with that, they want the room to be cleaned up by the end of the week. The care for the result asynchronously. Especially the part where it says: will FetchAsync this would work, but I myself am not to sure if "will archive" is the right style to use. Maybe renaming the variable could help, so something like: if (haveArchived != null) will haveArchived; haveArchived = ArchiveAsync(document); What do you think? bye, @cessorAnonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
+1 for "return until" I think "return" is totally the key point in this stuff, and marks the difference between "block until the async stuff completes" and "return to the caller, and resume the method after the async stuff completes".Anonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
@Blake: from Eric's post (bold text added) “if the task we are awaiting has not yet completed then sign up the rest of this method as the continuation of that task, and then RETURN TO YOUR CALLER immediately; the task will invoke the continuation when it completes.”Anonymous
October 29, 2010
Oh and one more after a quick dictionary lookup. give way until Perhaps better for non English speakers? I still prefer "defer" thoughAnonymous
October 29, 2010
@Filini - yes as far as it goes, but async methods are meant to be composed. Just as the async method you are writing uses "await" to call various async methods in it's body, the caller will be using 'await' to call you. This is the primary benefit, letting the whole call tree be structured in a simple, easy-to-reason-about fashion. Only at the very bottom (where asyncs are being built out of platform primitives), and at the very top (where an entire async workflow is being kicked off with a TaskEx.Run or such) will this pattern not hold true.Anonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
I thought that the [Asynchronous] attribute meant "compiler, do not optimise this routine to share registers or any other clever stuff like that, because this routine is, well, asynchronous, and is liable to fire at any old time, so any path analysis that the optimiser might want to do will be misleading."Anonymous
October 29, 2010
Personally (and subjectively), I kinda like the 'keyword' used in Scheme. It is called 'force'. I think F# uses 'do!' which I think is even better, but impossible for C#'s lexical syntax.Anonymous
October 29, 2010
The comment has been removedAnonymous
October 29, 2010
I like Stuart's keyword suggestion: use "after" instead of "await". Almost impossible to misunderstand, even for non-native speakers.Anonymous
October 30, 2010
"upon" var document = upon FetchAsync(urls[i]);Anonymous
October 30, 2010
The comment has been removedAnonymous
October 30, 2010
First, I have no problem with async and can't think of anything that would feel better there to me. As for await, I was thinking about something like this: async void ArchiveDocuments(List<Url> urls) { Task archive = null; for(int i = 0; i < urls.Count; ++i) { continue after FetchAsync(urls[i]) let document = value; if (archive != null) continue after archive; archive = ArchiveAsync(document); } } Although while writing this comment I've started to talk myself out of that, because it uses several existing keywords in similar but significantly different ways to how they are used now. Maybe new keywords would be better than blurring the meaning of existing ones... async void ArchiveDocuments(List<Url> urls) { Task archive = null; for(int i = 0; i < urls.Count; ++i) { resume after FetchAsync(urls[i]) with document = result; if (archive != null) resume after archive; archive = ArchiveAsync(document); } }Anonymous
October 30, 2010
The comment has been removedAnonymous
October 30, 2010
The comment has been removedAnonymous
October 30, 2010
@pete.d: I don't believe that "await" without "async" is actually valid. From what I remember of what Anders said, "async" is really just to indicate to the compiler that it's an async method which might contain "await" - and to make it clearer to other human readers. An async method with no "await" will result in a compiler warning, but the task will be created in the same way as it would in the situation where every "await" actually completed synchronously. (I believe in the current implementation, the task to be returned is effectively created as soon as the async method is called.)Anonymous
October 30, 2010
++ for "continue with" await() has embedded "wait" which is the main source of confusionAnonymous
October 30, 2010
The comment has been removedAnonymous
October 30, 2010
The comment has been removedAnonymous
October 30, 2010
The comment has been removedAnonymous
October 30, 2010
Oh, and another comment, related to some of the other suggestions: I definitely prefer the current linear model over a block-oriented approach suggested by some of the other commenters. For me, the reason this adds value is that it offers a way to write and read the code that expresses the sequence of statements accurately but with a minimum of overhead. If a block-oriented syntax were desirable, it would be easy enough today to just write a library that uses methods accepting delegate references. We'd have no need for language support at all. It can be done as the language stands now, and would be just as awkward as incorporating a block-oriented approach into the language would be.Anonymous
October 30, 2010
(My apologies if this shows up twice…the blog said the message had been posted already, but the page was behaving a little oddly — the text of the comment didn't get cleared — and my comment never appeared. So I'm trying again, just in case). Hmmm…I'm just catching up on some of the other recent posts in this blog (I'm behind because of a MSDN/browser incompatibility issue that prevents me from seeing the articles in my usual browser). Last month, there was this article: blogs.msdn.com/.../ambiguous-optional-parentheses-part-three.aspx In it, Eric discusses how they might identify and deal with potential ambiguities introduced in new features. While I like the standalone "await" keyword, now I'm wondering if it doesn't introduce exactly the kind of ambiguity being discussed here. And if so, maybe that's why "async" was introduced as well (since it would remove the ambiguity)?Anonymous
October 30, 2010
My proposal is to make the keyword names express the programmer intention and not the underlined technical meaning. Therefore I would propose to remove the "async" keyword from the method signature (because I don't see the need in it neither from compiler nor from programmer perspective), instead I would use "async" in place of await to express that that method will be invoked asynchronously. In general this idea tries to align the feature usage with "yield" feature usage.Anonymous
October 30, 2010
"Therefore I would propose to remove the "async" keyword from the method signature (because I don't see the need in it neither from compiler nor from programmer perspective), instead I would use "async" in place of await to express that that method will be invoked asynchronously." As I've mentioned in my comments, I agree that I don't see the need for "async". But that may just be because there's a need I don't see, rather than there not being a need. More to the point here though, is that "async" isn't a suitable alternative to "await", for an important reason already stated previously: the invocation of the target of the special word ("await" or "async" or whatever) is not actually guaranteed to be executed asynchronously. The "async" keyword is definitely misleading in that context.Anonymous
October 30, 2010
You are instructing the system to perform an action when something is done, therefore I think that "when" is the appropriate keyword. var document = await FetchAsync(urls[i]);Anonymous
October 30, 2010
You are instructing the system to perform an action when something is done, therefore I think that "when" is the appropriate keyword. var document = when FetchAsync(urls[i]);Anonymous
October 30, 2010
The comment has been removedAnonymous
October 30, 2010
As I mentioned, I think "await" is fine. But, having had more time to consider the question, and keeping in mind the desire for this to reflect the "what" rather than the "how", I think "await" and all the similar syntaxes aren't quite right. At the moment, my preferred syntax would be: • No "async" at all (i.e. method declaration is untouched…"async" is inferred from the body, like iterators) • The word "future" instead of "await" This is more declarative than imperative. That is, rather than telling the compiler "here's where we wait", you're telling the compiler that you want to assign the future value of something. As in, "I know the value's not necessarily going to be available right this minute…I want whatever the value of it is in the future though". I saw another suggestion that argues in favor of no new keywords at all, and for the compiler to infer from usage what to do. That actually seems feasible to me — the plain vanilla scenario would be "assigning a Task<T> to a T would infer an 'await' [or 'future' or whatever it's called]" — but I'm sure I'm being naïve about it. :)Anonymous
October 30, 2010
The comment has been removedAnonymous
October 30, 2010
For me the best term is "watch" like in watchman or watchtower. For me the statement says "continue the code while I watch that variable here"Anonymous
October 30, 2010
Hi, please explain why need "async" modifier, because we don't need "iterator" modifier when using "yield return/break". Why not just generate Task when the method body has await expressions and returns Task/Task<T>/void?Anonymous
October 30, 2010
I like 'yield until', which someone proposed earlier in the comments. 'Yield' is nice because it captures the concept of yielding control to the calling function without implying blocking. 'Until' is nice too, because it focuses the reader's attention on the asynchronous method, not on the code beyond it. (I'm not a fan of 'continue' or 'after' in this context. Continue is too reminiscent of the existing continue statement, and 'after' focuses your attention on the code that follows the asynchronous method.)Anonymous
October 30, 2010
@Beevik: You don't like continue because of the existing continue statement, but you do like yield? I don't see how either is better than the other in that regard.Anonymous
October 30, 2010
I'm also in favor to remove 'async' completely to be coherent with how iterators work. As for 'await', as your usability search proved, it's quite confusing the first time. However, and it's the case for any new feature, once you got accustomed to it that's not really a problem anymore. That being said, I would prefer the keyword 'complete' instead of 'await' or any 'yield'/'continue' combination. We want that task completed, whether it already was or will be. I think the implied "wait if you have to" is quite clear with this word.Anonymous
October 30, 2010
Just thought I'd remind people that Eric said in the original post "At a later date I’ll discuss the reasons behind requiring the async modifier rather than inferring it." May as well leave comments about removing that part entirely until that blog post...Anonymous
October 30, 2010
@fatcat1111: Using "when" seems to indicate that only the next action is done when the result is available. Really, we need to think about this in terms of the flow of the code. In terms of that, the code DOES wait before moving on, as in: the next code is waiting for the result before it's executed. True, it's not busy-waiting or blocking the thread. But in terms of code-flow, it's certainly waiting. Also, to the people suggestions scoped await's: this breaks down a bit when you consider loops, where you actually jump back, to before the scope was created, but it's still part of the continuation. Again, think in terms of flow of the code you're writing, not so much in terms of the underlying threading model.Anonymous
October 30, 2010
The biggest question remains for me is: throttling. How can i say that i want to download 10 archive in the same time from all the 100000 urls?Anonymous
October 31, 2010
Because of the close relationship between the async model and iterator blocks, the following exercise might be instructive. This code example uses async and await instead of yield return. Provide an implementation of AsyncEnumerator<T>. public class EnumerableRange : IEnumerable<int> { int start; int end; public EnumerableRange(int start, int end) { this.start = start; this.end = end; } private async Task Enumerate(AsyncEnumerator<int> enumerator) { for (int i = this.start; i < this.end; i++) { if (!await enumerator.YieldReturn(i)) { break; } } enumerator.Finished(); } public IEnumerator<int> GetEnumerator() { return new AsyncEnumerator<int>(Enumerate); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } }Anonymous
October 31, 2010
@Szindbad - there's an example in the 101 samples in the ctp download - look under CombinatorsAnonymous
October 31, 2010
>> The “async” ... “this method contains control flow that involves awaiting asynchronous operations and will therefore be rewritten by the compiler into continuation passing style to ensure that the asynchronous operations can resume this method at the right spot.” Why must I mark the method in this fashion? Can't the presence of the "await" keyword signal the necessary transformation in the same manner that the "yeld" keyword does today? As for "await", it reminds me of TSQL's WAITFOR except WAITFOR deals with time. -MikeAnonymous
October 31, 2010
>> The “async” ... “this method contains control flow that involves awaiting asynchronous operations and will therefore be rewritten by the compiler into continuation passing style to ensure that the asynchronous operations can resume this method at the right spot.” Why must I mark the method in this fashion? Can't the presence of the "await" keyword signal the necessary transformation in the same manner that the "yeld" keyword does today? As for "await", it reminds me of TSQL's WAITFOR except WAITFOR deals with time. -MikeAnonymous
October 31, 2010
I have read the comments and thought I would throw in a list of alternatives that come to mind, though the more I think about it async/await seems just fine, the following options are floating around in the back of my mind. yield async yield whileAnonymous
October 31, 2010
C# v.N+1 parody [Verify=true] [NewOperatorsEx] [EnableParallel] public lazy async parallel excuse foo const int<A,B:{42}>(int x) : await all bases<A>(x){i}<~[bar] { return await volatile yield [€ > $ > §] (x=7?) { sporadic return $x?; } ; }Anonymous
October 31, 2010
Let me get this straight. I need to type 6 extra characters "async " in every method just so some Fortune 5 company can get awayt without renaming their existing "await" lowercase things that don't conform to guidelines? If the async was a modifier that enabled a whole host of new operators and stuff in the future versions that broke all kinds of stuff to make nice things possible, it might be worth it. But you're selling it as enabling only the "await"? If the sole reason for adding this modifier is the risk of breaking existing code then can we have some proof that someone has publicly exposed API's with properties/interfaces that use lower case "await" in large code base? Lets give people a year to come up with their proof they're using it and if they don't then consider it as not in use.Anonymous
October 31, 2010
Brilliant design and excellent choice of keyword! I like the fact that I code the method body sequentially. When the code reaches the await point, it waits. That’s what it does. I don’t need to know that the control will be transferred back to the caller, nor do I need to know how or when. That’s what async is for. Just stick the async keyword on the method header and let the compiler does its magic. Very nice separation of concern! So please keep both keywords.Anonymous
October 31, 2010
Hi, another try I did is async iterator, I wrote the following code but it won't compile... async Task<IEnumerable<int>> IteratorAsync() { for ( int i = 0; i < 10; i++ ) { yield return await GetNum(i); } } private Task<int> GetNum( int i ) { return TaskEx.Run(() => i); } I changed the return type of IteratorAsync() to IEnumerable<Task<int>>, and that won't compile, too. I just want to use some code like: foreach ( var item in await IteratorAsync() ) { //.. }Anonymous
October 31, 2010
@diryboy I think there is an IAsyncEnumerable interface. Saw it here: blogs.msdn.com/.../a-technical-walk-through-all-of-the-async-ctp.aspxAnonymous
October 31, 2010
OK, I know why need "async" modifier, because async method body changes the "return". Iterators also changes "return", but it has a "yield" before it. In an async method that returns Task<T>, we actually write "return T" instead of "return Task<T>". And in the method body where it returns Task, no expressions should follow the return keyword (if there is). async static Task RunTask() { await TaskEx.Run(() => { for ( int i = 0; i < 10; i++ ) { } }); return; // actually no need "return" here though. }Anonymous
October 31, 2010
By the way... nobody mentioned anything about CCR - msdn.microsoft.com/.../bb648753.aspx shows a method returning an IEnumerator of ITask and using the existing yield return instead of a new await keyword. The async / await combo is simpler, true, but the Ports in CCR were also a good idea.Anonymous
October 31, 2010
The comment has been removedAnonymous
October 31, 2010
The comment has been removedAnonymous
October 31, 2010
I think "yield while" or "yield until" are the keywords that are the most appropriate to express the exact meaning of the intent, without suggesting any kind of block operation. P.S. I would really like to replace "lock" with "synchronize" or "sync". I know it's impossible at this stage, but would be really helpful. It would avoid lots of missusages, by C# newcomers.Anonymous
October 31, 2010
async definitely the best keyword in front of the method declaration. I'd go with async for the method call too as in: var document = async FetchAsync(urls[i]);Anonymous
October 31, 2010
I would vote for instead of "await" these ideas: "when" "proceed when" "until" "continue until" (favourite because it uses current meaning of "continue") "continue while" (then we would have the negative logical statement) async void ArchiveDocuments(List<Url> urls) { Task archive = null; for(int i = 0; i < urls.Count; ++i) { var document = continue until FetchAsync(urls[i]); if (archive != null) continue until archive; archive = ArchiveAsync(document); } }Anonymous
October 31, 2010
I'm not confident that I fully understand all the particulars of the new functionality, but what about "branch" for the keyword? It seems to me that the point here is to divide the control flow into two paths; the "main path" that we're enumerating ourselves and the "support path" that's handling some operation off in the background and will complete at an indeterminate point later. The "branch" might not last long-- it might not happen at all, in fact, if the asynchronous operation completes immediately-- but I think "branch" gets the point across that the we're splitting something off. But, as I said, I could be totally off the mark.Anonymous
November 01, 2010
I think async and await are fine. await is definitely better than anything that includes 'yield' in it. I understand the similarity of this construct to the IEnumerable yield but I don't think "yield" gives the semantics of what's going on. However, I'm looking forward to read the reasons behind requiring the async modifier rather than inferring it.Anonymous
November 01, 2010
The comment has been removedAnonymous
November 01, 2010
The comment has been removedAnonymous
November 01, 2010
There's a video interview with the async team on Channel 9 where it was said they have a 2nd version of the compiler which doesn't need the async modifier. On another msdn blog there is a slide that says the async modifier is only needed for VB. From that you can draw your own conclusions why they chose to add it to C# anyway. From the suggestions so far, if C# has to have both then I'll settle for async + await, atleast they start with the same letter which makes them stand out.Anonymous
November 01, 2010
i like async/async better than async/await.Anonymous
November 01, 2010
I think another reason "async" modifier should exist is that the Task of async method is kicked off when the method returns. Normal methods that return Task would not necessarily kick it off when the they return.Anonymous
November 01, 2010
WOW! Now all you need is to ask the Windows Azure team to guarantee the continuation is restored after machine fail-over. http://bit.ly/ae7DihAnonymous
November 01, 2010
The biggest compliant I have with the synax is that a keyword is embedded in an otherwise perfectly valid expression. As a corollary to this it breaks away from the spirit of how iterator blocks were designed. For example, "return yield value" is not legal synax, but "yield return value" certainly is and fits the intuitive pattern of a keyword followed by a continuation statement. Modelling the syntax of this new feature after the iterator syntax we might be better served with the following syntax. async void ArchiveDocuments(List<Url> urls) { Task archive = null; foreach (Url item in urls) { Document document; await FetchAsync(item) capture document; if (archive != null) await archive; archive = ArchiveAsync(document); } } Notice that I precede the continuation statement with theawait
keyword and use a new keyword 'capture' to capture the return value into a local variable. I am envisioning several different variations for capturing that return value, but the point I want to drive home is that the keyword that gets everything "going" precedes the statements it acts upon instead of getting embedded in it. Also, I vote to abolish the 'async' keyword if it is deemed sufficient that it could be inferred by the compiler. I am, of course, prepared to retract my vote if there is some complusory reason for its existence.Anonymous
November 02, 2010
How about callback for the keyword?Anonymous
November 02, 2010
Why do keywords need to be actual English words? In this instance, that seems to be causing some problems, because most of the candidates have meanings or associations in regular usage that are irrelvant or misleading. Suggestion: use "cwith" instead of "await". It is pronounceable and reasonably memorable, short and easy to type, and relatively free of stray associations. (I created it by contracting "continue with", which is my favorite of the suggestions so far.) If you don't like that one, try other neologisms / contractions / invented words.Anonymous
November 03, 2010
What about symbol instead of 'await'. Something like <> or ><. var document = <> FetchAsync(<> GetIdAsync());Anonymous
November 03, 2010
I think 'async' and 'await' are perfect. They're short and simple, they comprise only one keyword apiece (two for the entire feature set), and they both start with 'a', which implies they work together. 'yield for' takes two completely unrelated keywords and implements them in a non-intuitive way for which neither was intended; 'continue' is already used in a synchronous context and should not be re-used for something quite the opposite. 'after' doesn't even make since to me; you're not executing the method "after" anything, you're calling it immediately and then doing other stuff while you wait for it to finish executing. I say stick with the a's.Anonymous
November 04, 2010
Read most but not all comments. One of the first thing that occurred to me was "yield pending". Apologies if it's already been put out there.Anonymous
November 04, 2010
Typo in "which is then somehow asynchronously fetches": spurious "is". Awaiter, there's a bug in my spaghetti code.Anonymous
November 05, 2010
(Too many comment here, so I didn't read them all, my suggestion might have already been given.) Why not, instead of "await", just use .... "async" again ?Anonymous
November 05, 2010
(Too many comment here, so I didn't read them all, my suggestion might have been already been given.) Why not, instead of "await", just use .... "async" again ?Anonymous
November 06, 2010
Since the function is being modified to return a task, why not use "task" as the keyword? "finish" would work well instead of "await", since you need the function to be completed, but you don't necessarily need to wait.Anonymous
November 07, 2010
Eric! How about "await return". It makes it clearer that the method returns here, and mirrors "yield return" exactly. Cheers, PeteAnonymous
November 07, 2010
I think "continue" or "continue after" or "yield" are far, far, far better choices than "await". "await" simply reeks of blocking and I would be fascinated to understand why the other choices were all discarded.Anonymous
November 08, 2010
I very much like "yield until". Await feels like a blocking call. yield, tell me that control flow is returning to the caller, just like in an iterator block, and 'until' is telling me when control is returning to this block again "go away until this thing is done".Anonymous
November 09, 2010
The comment has been removedAnonymous
November 09, 2010
I agree with all those that were confused with the the name used for this new "await" operator. Plato has said that we are at the beginning of wisdom when we start visiting and exploring the names. So my proposal is to use the word "anathesis" [of greek origin] which means "assign a job and return". The fact that is not well known to English speaking people is good because they will oblige most of the readers to look for its meaning and as a result they will not be confused by the preconceptions they already have. On the other hand, the "anathesis" operator will pair perfectly with the "async" keyword which is also of greek origin.Anonymous
November 22, 2010
var document = continuation FetchAsync(urls[i]);Anonymous
November 24, 2010
Why two different word - await and async. Couldn't people just get by with one - async - for both methods and calls? BTW, async in calls in by fa the least confusing (compared to await at least :)Anonymous
November 30, 2010
The comment has been removedAnonymous
December 06, 2010
I'm fine with "await". As alpha coders I believe we tend to think too much about the "how" (and in excruciating detail). If you look at these async methods, they're kind of like work flows that operate over discontiguous chunks of time and possibly threads. So if my workflow says that a result is awaiting the completion of a certain async operation that make sense to me. If you want to get pedantic about keywords reflecting reality, then seems like there should be an uproar over the use of "return" in one of the new async methods. After all, the method will typically return well before it hits the "return" statement. But again, from a workflow point of value, that makes sense to me because it is returning the final result from the workflow. Now I'm not so sure about the need for "async" but if a modifier is absolutely needed, then I prefer "async".Anonymous
December 14, 2010
BTW "await" doesn't just yield the thread timeslice, it yields the thread itself! So "yield *" in its place would definitely make sense. My vote would be for "yield while" or "yield until" except when you consider that this new thread-agnostic method is destined to become the way all .NET asynchronous code is written... you would tire of writing that when a short "await" could be used in place.Anonymous
December 22, 2010
'eventually' - following the statement to yield with. var document = FetchAsync(urls[i]) eventually; if (archive != null) archive eventually; Alternatively, 'eventually then' var document = FetchAsync(urls[i]) eventually then; if (archive != null) archive eventually then; archive = ArchiveAsync(document);Anonymous
February 09, 2011
Eric, Here are my 2 cents at the first view of these new concepts replace async with 'blocked' and await with 'deferred' your code becomes: blocked void ArchiveDocuments(List<Url> urls) { Task archive = null; for(int i = 0; i < urls.Count; ++i) { var document = deferred FetchAsync(urls[i]); if (archive != null) deferred archive; archive = ArchiveAsync(document); } } the intent is decribed better in my opinion another good choice would be async -> 'finalized' and await -> 'nonfinalized'Anonymous
March 23, 2011
The comment has been removedAnonymous
May 05, 2011
Instead of 'wait' it should say 'resumable'...Anonymous
June 16, 2011
How about the using the keyword "start"?Anonymous
August 16, 2011
I would like to replace 'await' with 'async'. Then replace 'async' with 'task' or something else.Anonymous
September 28, 2011
I would have liked to see the keyword "defer" used in place of "await".Anonymous
November 01, 2011
My proposal: "afterwards" and simplify the syntax as follows: async void ArchiveDocuments(List<Url> urls) { Task archive = null; for(int i = 0; i < urls.Count; ++i) { var document = FetchAsync(urls[i]): afterftwerards archive: aftgerwards archive = ArchiveAsync(document); } } Do you like it?Anonymous
April 18, 2012
Nevermind, it just seems Part Two has a different title that the other parts...