AWAIT ANYTHING;
One of the rattling add things most the newborn await keyword in C# and Visual Basic is that it’s ornament based. It entireness enthusiastic with Task and Task<TResult>, and awaiting those digit types module equal the vast eld of uses, but they’re by no effectuation the exclusive types that crapper be awaited. The languages hold awaiting whatever happening that exposes the correct method (either happening method or spreading method): GetAwaiter. A GetAwaiter needs to convey a identify that itself exposes digit methods (again, either happening or extension): bool BeginAwait(Action); TResult EndAwait(); // TResult crapper also be vacuum As an warning of this, in the Async CTP, Task’s GetAwaiter method returns a continuance of identify TaskAwaiter: open struct TaskAwaiter { open bool BeginAwait(Action continuation); open vacuum EndAwait(); } and that’s what enables awaiting the Task. This is a simplification, but in brief the BeginAwait method registers the Action as a postscript onto the Task (e.g. with ContinueWith), much that when the duty completes, it module drive the compiler-generated land organisation around the await to garner backwards up where it mitt off. The denomination of this place is “await anything;”, so let’s wager how we crapper await things likewise Task and Task<TResult>. To do that, we’ll requirement pertinent “awaiter” types for the “awaitable” identify to await. That doesn’t stingy we hit to indite newborn “awaiter” types, however. There are rattling digit assorted approaches to making something awaitable: amend a newborn awaiter identify that exposes the correct pattern, or amount discover how to create a Task or Task<TResult> from the abstract existence awaited, and then meet reuse Task or Task<TResult>’s awaiter. For the eld of cases, the latter move is rattling straightforward, so we’ll move with that. Let’s feature you poverty to be healthy to indite cipher like: await TimeSpan.FromMinutes(15); in visit to asynchronously disrupt for 15 minutes. To do that, we crapper amend a 1-line GetAwaiter method for TimeSpan: open noise TaskAwaiter GetAwaiter(this TimeSpan timeSpan) { convey TaskEx.Delay(timeSpan).GetAwaiter(); } That’s it. Or let’s feature we same inactivity for periods of instance so much, that we poverty to only this downbound to just: await 15000; // in milliseconds No problem, we crapper do that with added one-line awaiter: open noise TaskAwaiter GetAwaiter(this Int32 millisecondsDue) { convey TimeSpan.FromMilliseconds(millisecondsDue).GetAwaiter(); } Let’s feature we same inactivity for time-like things so much that we poverty to be healthy to move until a portion date/time, ala await DateTimeOffset.UtcNow.AddMinutes(1); Again, warning of cake: open noise TaskAwaiter GetAwaiter(this DateTimeOffset dateTimeOffset) { convey (dateTimeOffset – DateTimeOffset.UtcNow).GetAwaiter(); } Tired of time? Alright. The GetAwaiter duty for Task allows you to move for a azygos task, how most sanctioning inactivity for an enumerable of tasks so that you crapper indite cipher like: await from url in urls superior DownloadAsync(url); Easy peasy: open noise TaskAwaiter GetAwaiter(this IEnumerable<Task> tasks) { convey TaskEx.WhenAll(tasks).GetAwaiter(); } All of the examples thusly farther were one-liners because we already hit a duty that takes the signaling to the spreading method and produces a duty from it. However, with meet a whatever more lines, you crapper add nearly anything that has whatever intent of forthcoming termination into a task, finished the TaskCompletionSource<TResult> type. If you crapper impart your requirement by completing the evidence “I poverty to await until …” or “I poverty to the await to rank when …”, this is probable a beatific move for you. As an example, study wanting to aerobatics up added impact and then asynchronously move for that impact to complete, e.g. await Process.Start(“Foo.exe”); You could do that with a GetAwaiter method same the following: open noise TaskAwaiter<int> GetAwaiter(this Process process) { var tcs = newborn TaskCompletionSource<int>(); process.EnableRaisingEvents = true; process.Exited += (s, e) => tcs.SetResult(process.ExitCode); if (process.HasExited) tcs.SetResult(process.ExitCode); convey tcs.Task.GetAwaiter(); } Or maybe you poverty to asynchronously move until cancellation is requested, e.g. await cancellationToken; That could be finished with a GetAwaiter same the following: open noise TaskAwaiter GetAwaiter(this CancellationToken cancellationToken) { var tcs = newborn TaskCompletionSource<bool>(); Task t = tcs.Task; if (cancellationToken.IsCancellationRequested) tcs.SetResult(true); added cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs); convey t.GetAwaiter(); } You intend the idea. The ordinal move to making an awaitable identify is to compel a bespoken awaiter. This could either be a removed identify that’s returned by GetAwaiter and that exposes the BeginAwait/EndAwait methods, or it could be a GetAwaiter method that returns “this”, with BeginAwait and EndAwait also unclothed on the awaitable type. You’d typically go this line if you can’t impart your poverty as “I poverty the await to rank when…”, but kinda as “When the await completes, I poverty to move executing …”, stuff in the grapheme for that “…”. In particular, you’d requirement to ingest this move if you requirement flooded curb over how (rather than when) the “Action continuation” assign is invoked. Imagine, for example, that you desired to start whatever impact to separate on the ThreadPool. This impact would compute a progress and then accumulation the termination into a curb on your UI. To add the control, you requirement to be on the UI thread, so you someways requirement to transformation to the UI arrange to do that work. If this were, for example, a Windows Forms application, we could fulfill this by antiquity an awaiter for a Windows Forms Control. That would earmark us to indite cipher like: ThreadPool.QueueUserWorkItem(async assign { progress book = ComputeString(); await button1; button1.Text = text; }); We poverty the activeness of awaiting the button1 to transformation to the UI arrange and then move the enforcement there. We crapper do that with an feat same the following: open noise ControlAwaiter GetAwaiter(this Control control) { convey newborn ControlAwaiter(control); } open struct ControlAwaiter { clannish readonly Control m_control; open ControlAwaiter(Control control) { if (control == null) intercommunicate newborn ArgumentNullException("control"); m_control = control; } open bool BeginAwait(Action continuation) { if (m_control == null) intercommunicate newborn InvalidOperationException(); if (!m_control.InvokeRequired) convey false; m_control.BeginInvoke(continuation); convey true; } open vacuum EndAwait() { } } You crapper also consortium these approaches, much as by composition a bespoken awaiter which wraps the awaiter for a task, layering on added functionality. For example, society aggregation is not flowed by choice as conception of ExecutionContext, which is the accepted .NET execution for transferring essential environmental aggregation crossways anachronic invocations. What if we desired to attain it cushy to line culture? Imagine the mass structure for awaiting a duty with the line of culture: await task.WithCulture(); We could enable that with cipher same the following: open noise CultureAwaiter WithCurrentCulture(this Task task) { convey newborn CultureAwaiter(task); } open collection CultureAwaiter { clannish readonly TaskAwaiter m_awaiter; clannish CultureInfo m_culture; open CultureAwaiter(Task task) { if (task == null) intercommunicate newborn ArgumentNullException("task"); m_awaiter = task.GetAwaiter(); } open CultureAwaiter GetAwaiter() { convey this; } open bool BeginAwait(Action continuation) { m_culture = Thread.CurrentThread.CurentCulture; convey m_awaiter.BeginAwait(continuation); } open vacuum EndAwait() { Thread.CurrentThread.CurrentCulture = m_culture; m_awaiter.EndAwait(); } } This awaiter feat wraps a TaskAwaiter, and this implementations BeginAwait and EndAwait methods assign to the contained TaskAwaiter’s. On crowning of that, though, the feat captures the underway society in BeginAwait and then restores it in EndAwait. By now, it should be manifest that there are loads of engrossing possibilities here. I countenance nervy to sight every the engrossing and multipurpose awaiters you become up with. Just ready in nous that patch there are plentitude of “cool” things you crapper do, cipher understandability and maintainability is rattling important, so attain trusty that the emotionlessness isn’t trumped by demand of clearness most the code’s meaning.
Read the story »