{"id":110857,"date":"2025-02-12T07:00:00","date_gmt":"2025-02-12T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110857"},"modified":"2025-02-12T13:51:36","modified_gmt":"2025-02-12T21:51:36","slug":"20250212-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250212-00\/?p=110857","title":{"rendered":"Async-Async revisited: What about cancellation?"},"content":{"rendered":"<p>We learned some time ago about <a title=\"Async-Async: Reducing the chattiness of cross-thread asynchronous operations\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190430-00\/?p=102460\"> Async-Async<\/a>, a feature that reduces the chattiness of cross-thread asynchronous operations. One thing we didn&#8217;t cover in the diagram is cancellation. How does cancellation work in Async-Async?<\/p>\n<p>Recall that Async-Async works by creating two helper objects, one on the client side that pretends to be the server, and one on the server side that pretends to be the client. When the client initiates an asynchronous operation on the server, it receives an <code>IAsyncOperation<\/code> object immediately: This object is the client side helper. That client side helper asynchronously contacts the server-side helper object, which itself initiates the actual asynchronous operation. This call is held pending until the actual asynchronous operation completes, at which point the cross-thread call is completed with the operation results.<\/p>\n<p>What happens when the operation is cancelled depends on when you cancel it.<\/p>\n<p>One possibility is that you manage to cancel the operation even before the client side helper can submit the call 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; width: 1em;\">\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;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/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>&nbsp;<\/td>\n<td>&nbsp;<\/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>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><code>Cancel()<\/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>&nbsp;<\/td>\n<td>&nbsp;<\/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>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/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<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>return Cancelled<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/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<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>fail with <code>ERROR_CANCELLED<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><code>callback<\/code> returns<\/td>\n<td>\u21e2<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td><code>Cancel()<\/code> returns<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/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>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>destroy fake <code>IAsync\u00adOperation<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In the case that the operation is cancelled by the client before any call goes out to the server, the client-side fake <code>IAsync\u00adOperation<\/code> just acts like a regular cancelled operation: It calls the callback to report that the operation was cancelled, and if anybody asks about the status or results of the operation, it says &#8220;It&#8217;s cancelled.&#8221;<\/p>\n<p>Another case is that the client layer has already initiated the operation on the server side. In that case, the client layer cancels the pending request, but it doesn&#8217;t wait for an answer from the server. It just reports the cancellation to the client right away and lets the server cancel the underlying operation in due course.<\/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; width: 1em;\">\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<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><code>Release()<\/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>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><code>Cancel()<\/code><\/td>\n<td>\u2192<\/td>\n<td>cancel pending call<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u2192<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/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>cancellation received<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/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<td><code>Cancel()<\/code><\/td>\n<td>\u2192<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>return Cancelled<\/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><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<td><code>Release()<\/code><\/td>\n<td>\u2192<\/td>\n<td><code>IAsyncOperation<\/code> is destroyed<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>fail with <code>ERROR_CANCELLED<\/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<\/td>\n<\/tr>\n<tr>\n<td><code>callback<\/code> returns<\/td>\n<td>\u21e2<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u21e0<\/td>\n<td>cancellation completes<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td><code>Cancel()<\/code> returns<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/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>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>destroy fake <code>IAsync\u00adOperation<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In the case of a cancellation that takes place after the operation has already been submitted to the server, the client cancels the pending call, which the server layer interprets as a request to cancel the server-side operation.<\/p>\n<p>Yet another case is that the server-side operation has completed before the client issues the cancellation. In that case, when the client layer cancels the pending request, it is told &#8220;What are you talking about? It&#8217;s alredy done!&#8221; The client then apologizes and carries on.<\/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; width: 1em;\">\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<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><code>Release()<\/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>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><code>Cancel()<\/code><\/td>\n<td>\u2192<\/td>\n<td>cancel pending call<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u2192<\/td>\n<td>cancellation received<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/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;\">\u2190<\/td>\n<td><code>Cancel()<\/code><\/td>\n<td>\u2192<\/td>\n<td>(nop &#8211; already completed)<\/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<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>return Cancelled<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u21e0<\/td>\n<td>cancellation completes<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/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<td><code>GetResults()<\/code><\/td>\n<td>\u2192<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>fail with <code>ERROR_CANCELLED<\/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 results<\/td>\n<\/tr>\n<tr>\n<td><code>callback<\/code> returns<\/td>\n<td>\u21e2<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td colspan=\"3\">try to return status and results<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td><code>Cancel()<\/code> returns<\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td colspan=\"3\">(nop &#8211; already cancelled)<\/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><tt>private<\/tt> returns<\/td>\n<td>\u21e2<\/td>\n<td><code>IAsyncOperation<\/code> is destroyed<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u21e0<\/td>\n<td>destroy fake <code>IAsync\u00adOperation<\/code><\/td>\n<td style=\"border-style: solid; border-width: 0px 1px;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>This behavior does not require any changes to the client or server. The fake operations introduced at the client and server behave just like a normal client and server. But if you are paying close attention, you may be able to observe some differences.<\/p>\n<p>One potentially observable behavior change is that when the client cancels the operation quickly, the server-side operation might never have started. If you&#8217;re correlating logs on the client and server side, you might see more client operations than server operations.<\/p>\n<p>Another potentially observable behavior change is that the server may appear to receive cancellations &#8220;late&#8221; because the client&#8217;s <code>Cancel<\/code> call returns immediately without waiting for the server-side cancellation to finish.<\/p>\n<p>Yet another potentially observable behavior change is that if the server receives the cancellation after it has completed the operation, the server&#8217;s logs will say that the operation ran successfully to completion, but the client&#8217;s logs will say that it cancelled the operation and received a <code>Canceled<\/code> status.<\/p>\n<p>Similarly, if the server receives the cancellation request and decides to complete the operation with <code>Completed<\/code> rather than cancelling, the client will still see <code>Canceled<\/code>.<\/p>\n<p>Async-Async virtualizes out both sides of an asynchronous operation, improving overall throughput and reducing chattiness within the contract of the behavior of asynchronous operations, but it can introduce observable effects if you rely on behavior outside the strict bounds of the contract.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Even the cancellation is async.<\/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-110857","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Even the cancellation is async.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110857","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=110857"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110857\/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=110857"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110857"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110857"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}