{"id":96665,"date":"2017-07-21T07:00:00","date_gmt":"2017-07-21T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=96665"},"modified":"2019-03-13T01:14:22","modified_gmt":"2019-03-13T08:14:22","slug":"20170721-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20170721-00\/?p=96665","title":{"rendered":"The perils of async void"},"content":{"rendered":"<p>We saw last time that <code>async void<\/code> is an odd beast, because it starts doing some work, and then returns as soon as it encounters an <code>await<\/code>, with the rest of the work taking place at some unknown point in the future. <\/p>\n<p>Why would you possibly want to do that? <\/p>\n<p>Usually it&#8217;s because you have no choice. For example, you may be subscribing to an event, and the event delegate assumes a synchronous handler. You want to do asynchronous work in the handler, so you use <code>async void<\/code> so that your handler has the correct signature, but you can still <code>await<\/code> in the function. <\/p>\n<p>The catch is that only the part of the function before the first <code>await<\/code> runs in the formal event handler. The rest runs after the formal event handler has returned. This is great if the event source doesn&#8217;t have requirements about what must happen before the handler returns. For example, the <code>Button.Click<\/code> event lets you know that the user clicked the button, but it doesn&#8217;t care when you finish processing. It&#8217;s just a notification. <\/p>\n<p>On the other hand, an event like <code>Suspending<\/code> assumes that when your event handler returns, it is okay to proceed with the suspend. But that may not be the case if your handler contains an <code>await<\/code>. The handler has not logically finished executing, but it did return from its handler, because the handler returned a <code>Task<\/code> which captures the continued execution of the function when the <code>await<\/code> completes. <\/p>\n<p>Aha, but you can fix this by making the delegate return a <code>Task<\/code>, and the event source would <code>await<\/code> on the task before concluding that the handler is ready to proceed. <\/p>\n<p>There are some problems with this plan, though. <\/p>\n<p>One problem is that making the event delegate return a <code>Task<\/code> is that the handler might not need to do anything asynchronous, but you force it to return a task anyway. The natural expression of this results in a compiler warning: <\/p>\n<pre>\n\/\/ Warning CS1998: This async method lacks 'await'\n\/\/ operators and will run synchronously.\nasync Task SuspendingHandler(object sender, SuspendingEventArgs e)\n{\n  \/\/ no await calls here\n}\n<\/pre>\n<p>To work around this, you need to add <code>return Task.CompletedTask;<\/code> to the end of the function, so that it returns a task that has already completed. <\/p>\n<p>A worse problem is that the return value from all but the last event handler is not used. <\/p>\n<blockquote CLASS=\"q\"><p>If the delegate invocation includes output parameters or a return value, <a HREF=\"https:\/\/msdn.microsoft.com\/library\/aa664605(v=vs.71).aspx\">their final value will come from the invocation of the last delegate in the list<\/a>. <\/p><\/blockquote>\n<p>(If there is no event handler, then attempting to raise the event results in a null reference exception.) <\/p>\n<p>So if there are multiple handlers, and each returns a <code>Task<\/code>, then only the last one counts. <\/p>\n<p>Which doesn&#8217;t seem all that useful. <\/p>\n<p>The Windows Runtime developed a solution to this problem, known as the Deferral Pattern. The event arguments passed to the event handler includes a method called <code>Get&shy;Deferral()<\/code>. This method returns a &#8220;deferral object&#8221; whose purpose in life is to keep the event handler &#8220;logically alive&#8221;. When you Complete the deferral object, then that tells the event source that the event handler has logically completed, and the event source can proceed. <\/p>\n<p>If your handler doesn&#8217;t perform any <code>await<\/code>s, then you don&#8217;t need to worry about the deferral. <\/p>\n<pre>\nvoid SuspendingHandler(object sender, SuspendingEventArgs e)\n{\n  \/\/ no await calls here\n}\n<\/pre>\n<p>If you do an <code>await<\/code>, you can take a deferral and complete it when you&#8217;re done. <\/p>\n<pre>\nasync void SuspendingHandler(object sender, SuspendingEventArgs e)\n{\n  var deferral = e.SuspendingOperation.GetDeferral();\n\n  \/\/ Even though there is an await, the suspending handler\n  \/\/ is logically still active because there is a deferral.\n  await SomethingAsync();\n\n  \/\/ Completing the deferral signals that the suspending\n  \/\/ handler is logically complete.\n  deferral.Complete();\n}\n<\/pre>\n<p>The <code>Suspending<\/code> event is a bit strange for historical reasons. <\/p>\n<p>Starting in Windows 10, there is <a HREF=\"https:\/\/docs.microsoft.com\/en-us\/uwp\/api\/Windows.Foundation.Deferral\">a standard Deferral object<\/a> which also supports <code>IDisposable<\/code>, so that you can use the <code>using<\/code> statement to complete the deferral automatically when control leaves the block. If the <code>Suspending<\/code> event were written today, you would be able to do this: <\/p>\n<pre>\nasync void SuspendingHandler(object sender, SuspendingEventArgs e)\n{\n  using (e.GetDeferral()) {\n\n    \/\/ Even though there is an await, the suspending handler\n    \/\/ is logically still active because there is a deferral.\n    await SomethingAsync();\n\n } \/\/ the deferral completes when code leaves the block\n}\n<\/pre>\n<p>Alas, we don&#8217;t yet have that time machine the Research division is working on, so the new <code>using<\/code>-based pattern works only for deferrals added in Windows 10. A <code>using<\/code>-friendly deferral will implement <code>IDisposable<\/code>. Fortunately, if you get it wrong and try to <code>using<\/code> a non-disposable deferral, the compiler will notice and report an error: &#8220;CS1674: type used in a using statement must be implicitly convertible to &#8216;System.IDisposable'&#8221;. <\/p>\n<p>And that&#8217;s the end of CLR We&#8230; no wait! CLR Week will continue into next week! What has the world come to!? <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Go on without me.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-96665","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Go on without me.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96665","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=96665"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96665\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=96665"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=96665"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=96665"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}