iamskok
iamskok

Reputation: 630

Inconsistent load order behavior for the resources loaded with <link rel="preload"> (Chrome)

I'm experimenting with dynamically adding <link rel="preload"> tag for loading font files based on the user connection navigator.connection.effectiveType, e.g. if effective type is '4g' inject <link as="font" type="font/woff2" rel="preload" crossorigin="anonymous" href="inter-var.woff2"> in the head tag before any other resources, if connection is 'slow-2g' / '2g' / '3g' don't inject link.

I'm also loading other resources using preload, but they are not as critical as fonts thus they are injected after the font files.

<head>
  // some other code
  <script id="connection-type-checker">
    (() => {
      const inter = document.createElement('link')
      const interItalic = document.createElement('link')
      const firaCode = document.createElement('link')

      inter.as = 'font'
      inter.type = 'font/woff2'
      inter.rel = 'preload'
      inter.crossOrigin = 'anonymous'
      inter.href = 'inter-var.woff2'

      interItalic.as = 'font'
      interItalic.type = 'font/woff2'
      interItalic.rel = 'preload'
      interItalic.crossOrigin = 'anonymous'
      interItalic.href = 'inter-var-italic.woff2'

      firaCode.as = 'font'
      firaCode.type = 'font/woff2'
      firaCode.rel = 'preload'
      firaCode.crossOrigin = 'anonymous'
      firaCode.href = 'fira-code.woff2'

      const insertAfter = (newNode, referenceNode) => referenceNode
        .parentNode.insertBefore(newNode, referenceNode.nextSibling)

      const target = document.getElementById('connection-type-checker')

      insertAfter(inter, target)
      insertAfter(interItalic, target)
      insertAfter(firaCode, target)
    })()
  </script>

  // **This is where <link>s get injected**

  // some other code...

  <link as="script" rel="preload" href="script.js" crossorigin="anonymous">
</head>

The problem I'm facing is that Chrome doesn't keep the original order for resources loaded with link preload if the link element was created with JavaScript (if link elements are inlined as HTML in the head tag everything works as expected).

Screenshot:

Safari, Chrome, Firefox

I'm trying to understand why the original order is breaking in Chrome and if it can be fixed?

Upvotes: 3

Views: 304

Answers (1)

Dominic Farolino
Dominic Farolino

Reputation: 1383

Chrome engineer here. There are a few inconsistencies here, from what I can tell:

  1. First, you can see Firefox is actually fetching the image.jpeg before the preloads as well, given your picture
  2. Second, I can actually occasionally get Safari to produce an odd ordering too:

Safari ordering

What you're seeing here is Chrome's (and some other browsers') background HTML parser at work, which is just another lightweight parser that quickly rips through the page looking for resources to speculatively (early) fetch. From debugging Chrome a bit on the example page you've created, the order of events happening is roughly:

  1. Speculative parser kicks off a request for script.js
  2. Speculative parser kicks off a request for image.jpeg
  3. Normal parser executes the document's pending parsing-blocking script
  4. The three preloads are fetched, in the order they were added

So in this case you're seeing the speculative/background parser jump ahead a bit, and start fetching things before the main parser is able to via your script tag. Most browsers have this kind of parser here's an article about it, and unfortunately it is not specified by any standard, since it is not supposed to have any observable (to application code) effects.

However, if you're seeing a performance problem due to this, your best bet is to file a Chromium bug on https://crbug.com and maybe let me know and I can get it in front of the right people.

P.S. I noticed you filed https://github.com/w3c/preload/issues/146, which I'll probably close in favor of you filing a Chrome bug if that's OK.

Upvotes: 1

Related Questions