{"id":102460,"date":"2019-04-30T07:00:00","date_gmt":"2019-04-30T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102460"},"modified":"2019-06-06T17:46:44","modified_gmt":"2019-06-07T00:46:44","slug":"20190430-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190430-00\/?p=102460","title":{"rendered":"Async-Async: Reducing the chattiness of cross-thread asynchronous operations"},"content":{"rendered":"<p>The Windows Runtime expresses the concept of asynchronous activity with the <code>IAsync\u00adOperation&lt;T&gt;<\/code> and <code>IAsyncAction<\/code> interfaces. The former represents an operation that completes asynchronously with a result of type <code>T<\/code>. The latter represents an operation that completes asynchronously with no result; you can think of it as <code>IAsync\u00adOperation&lt;void&gt;<\/code>. In fact, let&#8217;s just treat it as such for the purpose of this discussion.<\/p>\n<p>When you call a method like <code>Do\u00adSomething\u00adAsync<\/code>, it returns an instance of the <code>IAsync\u00adOperation<\/code> interface. All of the details of the <code>IAsync\u00adOperation<\/code> interface are normally hidden from the developer by the language projection. If you are writing in C#, you see a <code>Task<\/code>; in JavaScript, you get a <code>Promise<\/code>. In C++\/WinRT and C++\/CX, you <code>co_await<\/code> <code>IAsync\u00adOperation<\/code>, and the <code>co_await<\/code> machinery hides the details. In C++\/CX, you can also convert the <code>IAsync\u00adOperation<\/code> into a <code>concurrency::<\/code><code>task<\/code>, and then schedule your continuation that way.<\/p>\n<p>But today, we&#8217;re going to look at how things work under the covers.<\/p>\n<p>At the raw interface level, asynchronous operations work like this. In the diagrams, a solid arrow represents a call, and a dotted arrow represents the return from that call.<\/p>\n<table class=\"cp3\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Client<\/th>\n<th style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/th>\n<th>Server<\/th>\n<\/tr>\n<tr>\n<td><code>Do\u00adSomething\u00adAsync()<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u2192<\/td>\n<td>Start operation<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u21e0<\/td>\n<td>return <code>IAsync\u00adOperation<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>put_Completed(callback)<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u2192<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u21e0<\/td>\n<td>return<\/td>\n<\/tr>\n<tr>\n<td><code>Release()<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u2192<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u21e0<\/td>\n<td>release <code>IAsync\u00adOperation<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\" colspan=\"3\">&#8230; time passes &#8230;<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\" colspan=\"3\">Operation completes<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u2190<\/td>\n<td><code>callback.Invoke()<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>get_Status()<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u2192<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u21e0<\/td>\n<td>return Completed (or Error)<\/td>\n<\/tr>\n<tr>\n<td><code>GetResults()<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u2192<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u21e0<\/td>\n<td>return results<\/td>\n<\/tr>\n<tr>\n<td><code>callback<\/code> returns<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u21e2<\/td>\n<td><code>IAsync\u00adOperations<\/code> is destroyed<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>When the client calls the <code>Do\u00adSomething\u00adAsync()<\/code> method, the call is sent to the server, which starts the operation and returns an <code>IAsync\u00adOperation<\/code> which represents the operation in progress.<\/p>\n<p>The client calls the <code>IAsync\u00adOperation::<\/code><code>put_<\/code><code>Completed<\/code> method to specify a callback that will be invoked when the operation is complete, thereby allowing the client to resume execution when the operation is complete. The server saves this callback and returns.<\/p>\n<p>The client releases the <code>IAsync\u00adOperation<\/code>, since it no longer needs it. The operation itself keeps the <code>IAsync\u00adOperation<\/code> alive.<\/p>\n<p>Time passes, and eventually the operation is complete.<\/p>\n<p>The server invokes the callback to let it know that the operation is complete. The client receives a reference to the original <code>IAsync\u00adOperation<\/code> as part of the callback. The client can interrogate the <code>IAsync\u00adOperation<\/code> to determine whether the operation was successful or not, and if successful, what the result was.<\/p>\n<p>Finally, when the callback returns, there are no more outstanding reference to the <code>IAsync\u00adOperation<\/code>, so it destroys itself.<\/p>\n<p>You may have noticed that this is a very <a href=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20160212-00\/?p=93013\"> chatty<\/a> interface between the client and server. I mean, look at all those arrows!<\/p>\n<p>Enter Async-Async.<\/p>\n<p>Async-Async interposes layers on both the client and server which do local caching. The layer returns a fake async operation to the client and provides a fake client to the server.<\/p>\n<table class=\"cp3\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Client<\/th>\n<th>\u00a0<\/th>\n<th>Client Layer<\/th>\n<th style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/th>\n<th>Server Layer<\/th>\n<th>\u00a0<\/th>\n<th>Server<\/th>\n<\/tr>\n<tr>\n<td><code>Do\u00adSomething\u00adAsync()<\/code><\/td>\n<td>\u2192<\/td>\n<td>create fake <code>IAsyncOperation<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>return fake <code>IAsyncOperation<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u2192<\/td>\n<td>fake client<\/td>\n<td>\u2192<\/td>\n<td>Start operation<\/td>\n<\/tr>\n<tr>\n<td><code>put_Completed(callback)<\/code><\/td>\n<td>\u2192<\/td>\n<td>save in fake <code>IAsyncOperation<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>return <code>IAsyncOperation<\/code><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>return<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td><code>put_Completed(private)<\/code><\/td>\n<td>\u2192<\/td>\n<\/tr>\n<tr>\n<td><code>Release()<\/code><\/td>\n<td>\u2192<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>return<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>release fake <code>IAsync\u00adOperation<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td><code>Release()<\/code><\/td>\n<td>\u2192<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>release <code>IAsync\u00adOperation<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\" colspan=\"7\">&#8230; time passes &#8230;<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\" colspan=\"7\">Operation completes<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>\u2190<\/td>\n<td><code>private.Invoke()<\/code><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td><code>get_Status()<\/code><\/td>\n<td>\u2192<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>return Completed (or Error)<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td><code>GetResults()<\/code><\/td>\n<td>\u2192<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>return results<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u21e0<\/td>\n<td>return status and results<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>cache status and results<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u2190<\/td>\n<td><code>callback.Invoke()<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td><code>private<\/code> returns<\/td>\n<td>\u21e2<\/td>\n<td><code>IAsyncOperation<\/code> is destroyed<\/td>\n<\/tr>\n<tr>\n<td><code>get_Status()<\/code><\/td>\n<td>\u2192<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>return cached status<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td><code>GetResults()<\/code><\/td>\n<td>\u2192<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>return cached results<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td><code>callback<\/code> returns<\/td>\n<td>\u21e2<\/td>\n<td>fake <code>IAsync\u00adOperation<\/code> is destroyed<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>With Async-Async, the client&#8217;s call to <code>Do\u00adSomething\u00adAsync()<\/code> creates a fake <code>IAsync\u00adOperation<\/code> on the client side. This fake <code>IAsync\u00adOperation<\/code> makes a call out to the server to initiate the operation, but doesn&#8217;t wait for the server to respond to the request. Instead, the fake <code>IAsync\u00adOperation<\/code> immediately returns to the client.<\/p>\n<p>As before, the client calls <code>IAsync\u00adOperation::<\/code><code>put_<\/code><code>Completed<\/code> method to specify a callback that will be invoked when the operation is complete, thereby allowing the client to resume execution when the operation is complete. The fake <code>IAsync\u00adOperation<\/code> saves this callback and returns.<\/p>\n<p>The client releases the fake <code>IAsync\u00adOperation<\/code>, since it no longer needs it. The operation itself keeps the <code>IAsync\u00adOperation<\/code> alive.<\/p>\n<p>Meanwhile, the request from the fake <code>IAsync\u00adOperation<\/code> reaches the server, where a fake client is constructed. This fake client asks the real server to start the operation, and it registers its own private callback to be notified when the operation is complete, and then it releases the <code>IAsync\u00adOperation<\/code>.<\/p>\n<p>Time passes, and eventually the operation is complete.<\/p>\n<p>The server invokes the callback to notify the fake client that the operation is complete. The fake client immediately retrieves the status and result, and transmits both to the fake <code>IAsync\u00adOperation<\/code>, thereby completing the asynchronous call that was initiated by the fake <code>IAsync\u00adOperation<\/code> at the start.<\/p>\n<p>The fake client then returns from its callback, and everything on the server side is now all done.<\/p>\n<p>Meanwhile, the fake <code>IAsync\u00adOperation<\/code> has received the operation&#8217;s status and result and invokes the client&#8217;s callback. As before, the client calls the <code>IAsync\u00adOperation::<\/code><code>get_<\/code><code>Status()<\/code> method to find out whether the operation was successful or not, and it calls the <code>IAsync\u00adOperation::<\/code><code>Get\u00adResults()<\/code> method to obtain the results of the asynchronous operation from the fake <code>IAsync\u00adOperation<\/code>. The client returns from its callback, and everything on the client side is now all done.<\/p>\n<p>This interface is much less chatty. There is only one call from the client to the server (to start the operation), and only one call from the server back to the client (to indicate the status and result of the operation). All the rest of the calls are local and therefore fast.<\/p>\n<p>From the client&#8217;s perspective, Async-Async takes asynchronous operations and makes them even more asynchronous: Not only does the operation itself run asynchronously, even the <i>starting<\/i> of the operation takes place asynchronously. This gives control back to the client sooner, so it can do productive things like, say, running other ready tasks.<\/p>\n<p>Note that Async-Async comes into play only when the method call needs to be marshaled. If the client and server are on the same thread, then there is no need for Async-Async because the calls are all local already.<\/p>\n<p>Async-Async was introduced in Windows 10, and it is enabled for nearly all Windows-provided asynchronous operations. There are some methods that do not use Async-Async because they need to start synchronously; UI operations fall into this category.<\/p>\n<p>You can enable Async-Async for your own asynchronous operations by adding <a href=\"https:\/\/docs.microsoft.com\/en-us\/uwp\/api\/windows.foundation.metadata.remoteasyncattribute\"> the <code>[remote_async]<\/code> attribute<\/a> to your methods.<\/p>\n<pre>runtimeclass Awesome\r\n{\r\n  <span style=\"color: blue;\">[remote_async]<\/span>\r\n  Windows.Foundation.IAsyncAction BeAwesomeAsync();\r\n};\r\n<\/pre>\n<p>Although Async-Async is intended to be transparent to the client, there are some things to be aware of. We&#8217;ll look at those next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Setting up fakes on both sides.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[26],"class_list":["post-102460","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>Setting up fakes on both sides.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102460","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=102460"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102460\/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=102460"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102460"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102460"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}