<br />
<b>Warning</b>:  The magic method SFML_Singleton::__wakeup() must have public visibility in <b>/home/public/wp-content/plugins/sf-move-login/inc/classes/class-sfml-singleton.php</b> on line <b>72</b><br />
{"id":264,"date":"2018-06-15T07:45:30","date_gmt":"2018-06-15T13:45:30","guid":{"rendered":"http:\/\/www.munderwood.ca\/?p=264"},"modified":"2018-06-15T07:45:59","modified_gmt":"2018-06-15T13:45:59","slug":"side-effects-of-js-data-http-and-async-operations","status":"publish","type":"post","link":"https:\/\/www.munderwood.ca\/index.php\/2018\/06\/15\/side-effects-of-js-data-http-and-async-operations\/","title":{"rendered":"Side effects of js-data-http and async operations"},"content":{"rendered":"<p>Recently I encountered a rather strange scenario, wherein I created two different\u00a0<span class=\"lang:default decode:true crayon-inline \">findAll<\/span>\u00a0operations using the <a href=\"https:\/\/github.com\/js-data\/js-data-http\/tree\/3.0.0\">js-data-http<\/a> adapter for a <a href=\"http:\/\/www.js-data.io\/\">js-data<\/a> query, only to see two identical network requests emitted. The root of the problem turned out to be that I was creating a single object to hold the query options and reusing it across requests, in combination with the fact that js-data-http mutates its parameters in place during asynchronous operation.<\/p>\n<pre class=\"lang:js decode:true\">\/\/ This will find posts from user 4 twice!\r\nconst opts = { force: true };\r\nstore.findAll('post', { userId: 2 }, opts);\r\nstore.findAll('post', { userId: 4 }, opts);<\/pre>\n<p>Here is a brief <a href=\"https:\/\/jsfiddle.net\/munderwood\/by5oawgv\/\">demonstration of the behaviour<\/a>. In the fiddle two requests are made to the <a href=\"https:\/\/jsonplaceholder.typicode.com\">JSONPlaceholder test API<\/a>, for posts from different users. When the responses are received, they both contain the posts for the user specified in the second query.<\/p>\n<p>The simple solution is to not use the same object for multiple requests.<\/p>\n<pre class=\"lang:js decode:true \">\/\/ This works as expected.\r\nstore.findAll('post', { userId: 2 }, { force: true });\r\nstore.findAll('post', { userId: 4 }, { force: true });<\/pre>\n<h2>Why does this work?<\/h2>\n<p>Or more interestingly, why <em>doesn&#8217;t<\/em> the first method work?<\/p>\n<p>Part of the process js-data-http uses is to move the query object passed as the second argument to\u00a0<span class=\"lang:default decode:true crayon-inline\">findAll<\/span>\u00a0into a new property of the third argument,\u00a0<span class=\"lang:default decode:true crayon-inline\">options.params<\/span>. This is because the function that actually makes the network request expects any query parameters to be specified in\u00a0<span class=\"lang:default decode:true crayon-inline\">options.params<\/span>, at least in part because the query parameter format used in GET requests is not the same as the JSON-formatted <a href=\"http:\/\/www.js-data.io\/docs\/query-syntax\">query syntax<\/a> used by js-data. So <em>some<\/em> amount of transformation is required between what we pass in to js-data and what it sends to the server.<\/p>\n<p>However, because the transformation is done in place, js-data mutates the object that is passed into it. The chain of events goes roughly as follows, due to the asynchronous promise-based nature of the <span class=\"lang:default decode:true crayon-inline \">findAll<\/span>\u00a0 operation.<\/p>\n<ul>\n<li>An\u00a0<span class=\"lang:default decode:true crayon-inline \">options<\/span>\u00a0object is created in your code.<\/li>\n<li>The first\u00a0<span class=\"lang:default decode:true crayon-inline\">findAll<\/span>\u00a0process, Process A, begins, with Query A and Options A.<\/li>\n<li>The second\u00a0<span class=\"lang:default decode:true crayon-inline\">findAll<\/span>, Process B, begins, with Query B and Options B.<\/li>\n<li>Process A moves its query parameters onto the <span class=\"lang:default decode:true crayon-inline\">options<\/span>\u00a0object, so that\u00a0<span class=\"lang:default decode:true crayon-inline\">options.params<\/span>\u00a0contains (a transformed version of) Query A.<\/li>\n<li>Process B moves <em>its<\/em> query parameters onto <em>the same <\/em><span class=\"lang:default decode:true crayon-inline\">options<\/span>\u00a0object. Now\u00a0<span class=\"lang:default decode:true crayon-inline\">options.params<\/span> contains Query B, but it is still also being referenced from Process A!<\/li>\n<li>Process A makes its network request, based on Query B.<\/li>\n<li>Process B makes its network request as well, also based on Query B.<\/li>\n<li>Two responses to the second query are received.<\/li>\n<\/ul>\n<h2>Moral of the story<\/h2>\n<p>This turned out to be a very simple solution to a problem that required a very lengthy debugging process, all because I made the incorrect assumption that a third-party library would not have undocumented side effects on the variables that I passed into it. It was complicated by the fact that the side effects were themselves dependent on a different parameter, and of course by the async nature of the operations, but at its heart that was the issue.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently I encountered a rather strange scenario, wherein I created two different\u00a0findAll\u00a0operations using the js-data-http adapter for a js-data query, only to see two identical network requests emitted. The root of the problem turned out to be that I was creating a single object to hold the query options and reusing it across requests, in &#8230; <span class=\"more\"><a class=\"more-link\" href=\"https:\/\/www.munderwood.ca\/index.php\/2018\/06\/15\/side-effects-of-js-data-http-and-async-operations\/\">[Read more&#8230;]<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3,13],"tags":[17],"_links":{"self":[{"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/posts\/264"}],"collection":[{"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/comments?post=264"}],"version-history":[{"count":7,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/posts\/264\/revisions"}],"predecessor-version":[{"id":271,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/posts\/264\/revisions\/271"}],"wp:attachment":[{"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/media?parent=264"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/categories?post=264"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.munderwood.ca\/index.php\/wp-json\/wp\/v2\/tags?post=264"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}