{"id":102466,"date":"2019-05-02T07:00:00","date_gmt":"2019-05-02T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102466"},"modified":"2019-06-06T17:47:37","modified_gmt":"2019-06-07T00:47:37","slug":"20190502-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190502-00\/?p=102466","title":{"rendered":"Async-Async: Consequences for ordering of multiple calls in flight simultaneously"},"content":{"rendered":"<p>The feature known as Async-Async makes asynchronous operations even more asynchronous by pretending that they started before they actually did. As I noted earlier, you might notice a difference if you have been breaking the rules and getting away with it. <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190501-36\/?p=102463\"> We saw last time<\/a> what happens if you mutate a parameter that was passed to an asynchronous method. (Short answer: Don&#8217;t do that until the asynchronous method completes.)<\/p>\n<p>Another place you may notice a difference is if you race multiple calls against each other.<\/p>\n<pre>\/\/ Code in italics is wrong.\r\n\r\n\/\/ Queue two animations in sequence.\r\n\r\n\/\/ Queue the first animation.\r\nvar task1 = widget.QueueAnimationAsync(source1, curve1);\r\n\r\n\/\/ Queue the second animation.\r\n<i>var task2 = widget.QueueAnimationAsync(source2, curve2);<\/i>\r\n\r\n\/\/ Wait for the tasks to complete.\r\nawait Task.WhenAll(task1, task2);\r\n<\/pre>\n<p>This code &#8220;knows&#8221; that the <code>Widget.<\/code><code>Queue\u00adAnimation\u00adAsync<\/code> method chooses the point at which the animation will be added <i>before<\/i> it returns with an <code>IAsync\u00adOperation<\/code>. It therefore &#8220;knows&#8221; that it can queue two items in order by starting the <code>Queue\u00adAnimation\u00adAsync<\/code> operations in order, and waiting for both of them to complete.<\/p>\n<p>This code does not work when Async-Async is enabled because the two calls to <code>Widget.<\/code><code>Queue\u00adAnimation\u00adAsync<\/code> are not required to start on the server in the same order that the client issued them. The fake <code>IAsync\u00adOperation<\/code> issues a request to start the operation on the server, but does not wait for the server to acknowledge the start of the operation. If you start two operations, they race to the server, and the second one may reach the server first, in which case the operations will be started on the server in the opposite order.<\/p>\n<p>Of course, proper client code should not have had this dependency on the order of asynchronous operations in the first place. After all, the server might decide not to choose the position of the animations until later in the asynchronous operation, and the second operation may have raced to the decision point ahead of the first operation. For example, the internal behavior of <code>Queue\u00adAnimation\u00adAsync<\/code> may have been<\/p>\n<ol>\n<li>Create a new animation from the <code>source<\/code> and <code>curve<\/code> parameters.<\/li>\n<li>Add that animation to the list of animations.<\/li>\n<\/ol>\n<p>If you start two <code>Queue\u00adAnimation\u00adAsync<\/code> operations in parallel, you don&#8217;t really know which one will reach step 2 first.<\/p>\n<p>If the order of queueing is important, then you need to wait until the first animation is definitely queued before you queue the second one. You&#8217;ll have to run the operations in series, rather than in parallel.<\/p>\n<pre>\/\/ Queue two animations in sequence.\r\n\r\n\/\/ Queue the first animation.\r\nawait widget.QueueAnimationAsync(source1, curve1);\r\n\r\n\/\/ Queue the second animation.\r\n<span style=\"color: blue;\">await widget.QueueAnimationAsync(source2, curve2);<\/span>\r\n<\/pre>\n<p>In a sense, this is another case of &#8220;mutating a parameter passed to an asynchronous method&#8221;: The parameter that is being mutated is the <code>widget<\/code> itself! Well, more specifically, the animation queue of the widget.<\/p>\n<p>Next time, we&#8217;ll look at another consequence of Async-Async that you may notice if you have been cheating the rules.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You don&#8217;t know when they&#8217;ll reach the other side.<\/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-102466","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>You don&#8217;t know when they&#8217;ll reach the other side.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102466","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=102466"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102466\/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=102466"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102466"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102466"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}