4

Update:

I changed the API from fetch to XMLHttpRequest and I still see the problem.

According to the console logs the delay is between "readyState 1"(i.e. OPENED) and "readyState 2"(i.e HEADERS_RECEIVED).

Also, maybe it is worth mentioning, that in Firefox it works fine.


I would appreciate a couple of pointers on how to debug why the call to fetch() takes 1 sec in chrome.

According to "network" tab the request took only 12ms. However in my logs and the "timeline" tab the fetch() takes 1.06 sec. (Screenshots below).

Any tips on how to figure out what is stalling the fetch()?

Network Tab screenshot: enter image description here

Timeline Tab screenshot: enter image description here

  • Do you get any further information when you hover or click on the network panel entry? – Bergi Sep 21 '16 at 04:36
  • In the "Network" Tab, where the entry is normal, I get extra information, but everything looks fine there. In the "Timeline" Tab, I don't get anything extra if I click on the long entry. –  Sep 21 '16 at 05:07
  • second screenshot belongs to which tab? – Abhijeet Sep 21 '16 at 14:04
  • @Abhijeet sorry if it was not clear. The second screenshot is for the "Timeline" Tab, for the exact same request. –  Sep 21 '16 at 19:41
  • Have you tried disabling all extensions, and/or tested in Incognito mode? Also, check out `about:net-internals`, it may provide some clues as to what's going on exactly. – robertklep Sep 21 '16 at 19:51
  • @robertklep Yes, I have tried with all extensions disable, still see the problem. I tried looking at `about:net-internals`, and as far as I could tell it looks fine. I am starting to thing it is not a network issue, but something to do with the `promises`, as I use them pretty heavily –  Sep 21 '16 at 19:56
  • I would suspect that in that case, the issue would surface in other browsers too. The other tabs in the timeline entry for the fetch may also provide some clues (_Bottom-Up_ and _Event Log_ are most useful, probably). – robertklep Sep 21 '16 at 20:01
  • I have narrowed it down a bit more. It only happens when I scroll using my mousepad just before activating the link that does the transfer! –  Sep 22 '16 at 07:09
  • If I understand the timeline correctly, there _is_ an animation running during the same time that the fetch is taking place. Something hogging the event loop perhaps? – robertklep Sep 22 '16 at 07:20
  • Hey @robertklep. Yeap! You understand it absolutely correctly! Removing the animation doesn't help however. I tried it :( –  Sep 22 '16 at 07:29
  • One more clue.. The problem goes away if I set `async` to `false`. –  Sep 22 '16 at 08:17
  • There's only 1 client thread in JavaScript, so anything else blocking the thread will cause a delay. Move the operation to a WebWorker and the delay will go away, because nothing else will be trying to execute asynchronously on that thread. However, the delay might move to waiting on the response from your WebWorker. :) – TylerY86 Sep 26 '16 at 11:56
  • 1
    I'd love to make an answer for you, but you need to write a working example for it to be based on - without the context, an example may be pointless. – TylerY86 Sep 26 '16 at 11:58
  • 1) Maybe this time need to open and close http connection? – Alex Nikulin Sep 27 '16 at 06:32
  • I think it is either a bug in Chrome or in MDL. [Here](https://db.tt/22LpjpYm) is a minimal example where the problem is seen (it is written on elm, but I have seen the problem in plain JS as well). The only real code that I add is the XMLHTTPRequest. –  Sep 27 '16 at 06:47
  • @TylerY86 Thanks for the pointers to the WebWorker. Will give it a try! –  Sep 27 '16 at 06:48
  • Provided you with an example of using Fetch + WebWorker as an answer. Good luck. – TylerY86 Sep 27 '16 at 07:15
  • Elm is outside the tags listed in the question. Could you provide a minimal JS example? – TylerY86 Sep 27 '16 at 07:24

2 Answers2

5

I'm wagering since the latency goes away when you have async: false that it's due to something blocking the main thread.

Here, I get roughly 10 milliseconds for a simple request using Fetch inside of a WebWorker and posting the results back to the main thread.

var workerBlobs = Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return new Blob([oScript.textContent], {type: "text/javascript"}); });

var workers = workerBlobs.map(function(oBlob) { return new Worker(URL.createObjectURL(oBlob)); });

var log = document.getElementById('log');

function println(s) {
  log.appendChild(document.createTextNode(s+'\n'));
}
workers[0].onmessage = function(oEvent) {
 println("Done in " + ( performance.now() - start ).toFixed(4) + "ms" );
  println(oEvent.data.message);
}
workers[0].onerror = function(oEvent) {
  println("Error: "+JSON.stringify(oEvent.data));
}

var start = performance.now();
workers[0].postMessage('http://stacksnippets.net/');
#log {
  white-space: pre-wrap;
}
<script type="text/js-worker">
onmessage = function (oEvent) {
  fetch(oEvent.data).then(function(response) {
    response.text().then(function(text){
      console.log("response:",text);
      postMessage({ type: 'response', message: text });
    });
  }).catch(function(err) {
    console.error("error:",err);
    postMessage({ type: 'error', message: err.toString(), stack: err.stack });
  });
}
</script>
<pre id="log"></pre>

You'll have to excuse the error message when trying to execute; requests are somewhat cordoned inside that snippet sandbox. If I find a URL that works I'll throw it in. Use the jsfiddle.net example.

Edit: Updated jsfiddle.net example. There is some minor latency penalty incurred when using postMessage (in this example, approximately 5ms in chrome). Same 10ms average total. This might be worked around using transferrable array buffers and some other shenanigans.

To be more clear about this, you should be able to use this example to perform your fetch and timing on your worker thread, and then see how long it takes to get back to your main thread. If it takes far too much time to go from worker to main, something is blocking in the main JS thread. Then you have to hunt that down, and introduce some async flow to allow interspersion with your fetching and such.

TylerY86
  • 3,737
  • 16
  • 29
0

Turns out it was a Chrome bug that has now been fixed in Chrome 54: https://bugs.chromium.org/p/chromium/issues/detail?id=649590#c1

  • I don't think anyone said in that bug that it was a bug, just that you couldn't reproduce it in Chrome 54. Are you sure it isn't something you changed? jan.boes...@gmail.com commented that it wasn't fixed, that your issues were different. – TylerY86 Nov 06 '16 at 15:57
  • Nope, I didn't change anything. The same exact code works for Safari, Firefox and Chrome 54, but fails in chrome 53. –  Nov 07 '16 at 01:37