Загружать скрипты асинхронно

Я использую несколько плагинов, пользовательских виджетов и некоторых других библиотек из JQuery. В результате у меня есть несколько файлов.js и.css. Мне нужно создать загрузчик для моего сайта, потому что это требует времени для загрузки. было бы хорошо, если бы я мог отобразить загрузчик перед импортом всех:

<script type="text/javascript" src="js/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="js/myFunctions.js"></script>
<link type="text/css" href="css/main.css" rel="stylesheet" />
... 
....
 etc

Я нашел несколько учебных пособий, которые позволяют мне асинхронно импортировать библиотеку JavaScript. например я могу сделать что-то вроде:

  (function () {
        var s = document.createElement('script');
        s.type = 'text/javascript';
        s.async = true;
        s.src = 'js/jquery-ui-1.8.16.custom.min.js';
        var x = document.getElementsByTagName('script')[0];
        x.parentNode.insertBefore(s, x);
    })();

по какой-то причине, когда я делаю одно и то же для всех своих файлов, страницы не работают. Я так долго пытался найти, где проблема, но я просто не могу ее найти. Сначала я подумал, что это возможно потому, что некоторые функции javascript зависят от других. но я загрузил их в правильном порядке, используя функцию тайм-аута, когда одна завершена, я продолжил со следующей, и страница все еще ведет себя странно. например я не могу нажимать на ссылки и т.д... анимации все еще работают, хотя..

В любом случае

Вот о чем я думал... Я считаю, что браузеры имеют кеш, поэтому загрузка страницы в первый раз занимает много времени, а в следующий раз - быстро. так что я думаю о том, чтобы заменить мою страницу index.html страницей, которая загружает все эти файлы асинхронно. когда Ajax завершит загрузку всех этих файлов, перенаправьте на страницу, которую я планирую использовать. при использовании этой страницы загрузка не займет много времени, поскольку файлы должны быть уже включены в кеш браузера. на моей странице индекса (страница, где файлы.js и.css загружаются асинхронно), я не забочусь об ошибках. Я просто буду отображать загрузчик и перенаправлять страницу, когда закончите...

Является ли эта идея хорошей альтернативой? или я должен продолжать пытаться реализовать асинхронные методы?


РЕДАКТИРОВАТЬ

То, как я загружаю все асинхронно, выглядит так:

importScripts();

function importScripts()
{
    //import: jquery-ui-1.8.16.custom.min.js
    getContent("js/jquery-1.6.2.min.js",function (code) {
                var s = document.createElement('script');
                s.type = 'text/javascript';
                //s.async = true;
                s.innerHTML=code;
                var x = document.getElementsByTagName('script')[0];
                x.parentNode.insertBefore(s, x);
                setTimeout(insertNext1,1);
            });


    //import: jquery-ui-1.8.16.custom.min.js
    function insertNext1()
    {
        getContent("js/jquery-ui-1.8.16.custom.min.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext2,1);
                });
    }

    //import: jquery-ui-1.8.16.custom.css
    function insertNext2()
    {

        getContent("css/custom-theme/jquery-ui-1.8.16.custom.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext3,1);
                });
    }

    //import: main.css
    function insertNext3()
    {

        getContent("css/main.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext4,1);
                });
    }

    //import: jquery.imgpreload.min.js
    function insertNext4()
    {
        getContent("js/farinspace/jquery.imgpreload.min.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext5,1);
                });
    }


    //import: marquee.js
    function insertNext5()
    {
        getContent("js/marquee.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext6,1);
                });
    }


    //import: marquee.css
    function insertNext6()
    {

        getContent("css/marquee.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext,1);
                });
    }



    function insertNext()
    {
        setTimeout(pageReadyMan,10);        
    }
}


// get the content of url and pass that content to specified function
function getContent( url, callBackFunction )
{
     // attempt to create the XMLHttpRequest and make the request
     try
     {
        var asyncRequest; // variable to hold XMLHttpRequest object
        asyncRequest = new XMLHttpRequest(); // create request object

        // register event handler
        asyncRequest.onreadystatechange = function(){
            stateChange(asyncRequest, callBackFunction);
        } 
        asyncRequest.open( 'GET', url, true ); // prepare the request
        asyncRequest.send( null ); // send the request
     } // end try
     catch ( exception )
     {
        alert( 'Request failed.' );
     } // end catch
} // end function getContent

// call function whith content when ready
function stateChange(asyncRequest, callBackFunction)
{
     if ( asyncRequest.readyState == 4 && asyncRequest.status == 200 )
     {
           callBackFunction(asyncRequest.responseText);
     } // end if
} // end function stateChange

и странная часть в том, что вся работа стиля плюс все функции javascript. хотя страница почему-то заморожена...

22 ответа

Решение

Пара решений для асинхронной загрузки:

//this function will work cross-browser for loading scripts asynchronously
function loadScript(src, callback)
{
  var s,
      r,
      t;
  r = false;
  s = document.createElement('script');
  s.type = 'text/javascript';
  s.src = src;
  s.onload = s.onreadystatechange = function() {
    //console.log( this.readyState ); //uncomment this line to see which ready states are called.
    if ( !r && (!this.readyState || this.readyState == 'complete') )
    {
      r = true;
      callback();
    }
  };
  t = document.getElementsByTagName('script')[0];
  t.parentNode.insertBefore(s, t);
}

Если у вас уже есть jQuery на странице, просто используйте:

$.getScript(url, successCallback)*

Кроме того, возможно, что ваши скрипты загружаются / выполняются до завершения загрузки документа, а это означает, что вам нужно подождатьdocument.readyдо событий могут быть связаны с элементами.

Невозможно точно сказать, в чем ваша проблема, не видя код.

Самое простое решение - сохранить все ваши сценарии встроенными в нижней части страницы, чтобы они не блокировали загрузку содержимого HTML во время выполнения. Это также позволяет избежать асинхронной загрузки каждого необходимого сценария.

Если у вас есть необычное взаимодействие, которое не всегда используется и требует какого-то более крупного сценария, может быть полезно избежать загрузки этого конкретного сценария, пока он не понадобится (отложенная загрузка).

* скрипты, загруженные$.getScript скорее всего не будет кешироваться


Для тех, кто может использовать современные функции, такие какPromise объект, loadScript Функция стала значительно проще:

function loadScript(src) {
    return new Promise(function (resolve, reject) {
        var s;
        s = document.createElement('script');
        s.src = src;
        s.onload = resolve;
        s.onerror = reject;
        document.head.appendChild(s);
    });
}

Имейте в виду, что эта версия больше не принимает callback Аргумент в качестве возвращаемого обещания будет обрабатывать обратный вызов. Что раньше было бы loadScript(src, callback) теперь будет loadScript(src).then(callback),

Это имеет дополнительный бонус в способности обнаруживать и обрабатывать сбои, например, можно позвонить...

loadScript(cdnSource)
    .catch(loadScript.bind(null, localSource))
    .then(successCallback, failureCallback);

... и он будет корректно обрабатывать сбои CDN.

Предполагается, что новый атрибут HTML5'async' поможет. defer также поддерживается в большинстве браузеров, если вы заботитесь о IE.

асинхронный - HTML

<script async src="siteScript.js" onload="myInit()"></script>

отложить - HTML

<script defer src="siteScript.js" onload="myInit()"></script>

Анализируя новый код рекламного блока AdSense, я заметил, что атрибут и поиск привели меня сюда: http://davidwalsh.name/html5-async

Я загружал сценарии асинхронно (html 5 имеет эту функцию), когда все сценарии, для которых загрузка была завершена, перенаправили страницу в index2.html, где index2.html использует одни и те же библиотеки. Поскольку браузеры имеют кеш, когда страница перенаправляется на index2.html, index2.html загружается менее чем за секунду, потому что у него есть все, что нужно для загрузки страницы. На моей странице index.html я также загружаю изображения, которые планирую использовать, чтобы браузер помещал эти изображения в кэш. поэтому мой index.html выглядит так:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title>Project Management</title>

    <!-- the purpose of this page is to load all the scripts on the browsers cache so that pages can load fast from now on -->

    <script type="text/javascript">

        function stylesheet(url) {
            var s = document.createElement('link');
            s.type = 'text/css';
            s.async = true;
            s.src = url;
            var x = document.getElementsByTagName('head')[0];
            x.appendChild(s);
        }

        function script(url) {
            var s = document.createElement('script');
            s.type = 'text/javascript';
            s.async = true;
            s.src = url;
            var x = document.getElementsByTagName('head')[0];
            x.appendChild(s);
        }

        //load scritps to the catche of browser
        (function () {            
                stylesheet('css/custom-theme/jquery-ui-1.8.16.custom.css');
                stylesheet('css/main.css');
                stylesheet('css/marquee.css');
                stylesheet('css/mainTable.css');

                script('js/jquery-ui-1.8.16.custom.min.js');
                script('js/jquery-1.6.2.min.js');
                script('js/myFunctions.js');
                script('js/farinspace/jquery.imgpreload.min.js');
                script('js/marquee.js');            
        })();

    </script>

    <script type="text/javascript">
       // once the page is loaded go to index2.html
        window.onload = function () {
            document.location = "index2.html";
        }
    </script>

</head>
<body>

<div id="cover" style="position:fixed; left:0px; top:0px; width:100%; height:100%; background-color:Black; z-index:100;">Loading</div>

<img src="images/home/background.png" />
<img src="images/home/3.png"/>
<img src="images/home/6.jpg"/>
<img src="images/home/4.png"/>
<img src="images/home/5.png"/>
<img src="images/home/8.jpg"/>
<img src="images/home/9.jpg"/>
<img src="images/logo.png"/>
<img src="images/logo.png"/>
<img src="images/theme/contentBorder.png"/>

</body>
</html>

Еще одна приятная вещь в том, что я могу разместить загрузчик на странице, и когда страница будет загружена, загрузчик уйдет и через миллисекунды будет запущена новая страница.

Пример из Google

<script type="text/javascript">
  (function() {
    var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
    po.src = 'https://apis.google.com/js/plusone.js?onload=onLoadCallback';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
  })();
</script>

Несколько заметок:

  • s.async = true не очень правильно для HTML5 doctype, правильно s.async = 'async' (на самом деле используя true правильно, спасибо amn, который указал на это в комментарии чуть ниже)
  • Использование тайм-аутов для управления заказом не очень хорошо и безопасно, и вы также значительно увеличиваете время загрузки, чтобы оно равнялось сумме всех тайм-аутов!

Так как в последнее время есть причина загружать файлы асинхронно, но по порядку, я бы порекомендовал немного более функционально управляемый способ по вашему примеру (удалить console.log для производственного использования:)):

(function() {
    var prot = ("https:"===document.location.protocol?"https://":"http://");

    var scripts = [
        "path/to/first.js",
        "path/to/second.js",
        "path/to/third.js"
    ];

    function completed() { console.log('completed'); }  // FIXME: remove logs

    function checkStateAndCall(path, callback) {
        var _success = false;
        return function() {
            if (!_success && (!this.readyState || (this.readyState == 'complete'))) {
                _success = true;
                console.log(path, 'is ready'); // FIXME: remove logs
                callback();
            }
        };
    }

    function asyncLoadScripts(files) {
        function loadNext() { // chain element
            if (!files.length) completed();
            var path = files.shift();
            var scriptElm = document.createElement('script');
            scriptElm.type = 'text/javascript';
            scriptElm.async = true;
            scriptElm.src = prot+path;
            scriptElm.onload = scriptElm.onreadystatechange = \
                checkStateAndCall(path, loadNext); // load next file in chain when
                                                   // this one will be ready 
            var headElm = document.head || document.getElementsByTagName('head')[0];
            headElm.appendChild(scriptElm);
        }
        loadNext(); // start a chain
    }

    asyncLoadScripts(scripts);
})();

Благодаря HTML5 вы можете теперь объявить сценарии, которые вы хотите загрузить асинхронно, добавив "async" в тег:

<script async>...</script>

Примечание. Атрибут async предназначен только для внешних скриптов (и должен использоваться только при наличии атрибута src).

Примечание. Существует несколько способов выполнения внешнего скрипта:

  • Если присутствует асинхронность: сценарий выполняется асинхронно с остальной частью страницы (сценарий будет выполняться, пока страница продолжает анализ)
  • Если async отсутствует, а defer присутствует: скрипт выполняется после завершения анализа страницы
  • Если нет ни асинхронного, ни отсроченного: скрипт извлекается и выполняется немедленно, прежде чем браузер продолжит синтаксический анализ страницы.

Смотрите это: http://www.w3schools.com/tags/att_script_async.asp

Я написал небольшой пост, чтобы помочь с этим, вы можете прочитать больше здесь https://timber.io/snippets/asynchronously-load-a-script-in-the-browser-with-javascript/, но я приложил вспомогательный класс ниже. Он автоматически будет ждать загрузки скрипта и вернет указанный атрибут окна, как только это произойдет.

export default class ScriptLoader {
  constructor (options) {
    const { src, global, protocol = document.location.protocol } = options
    this.src = src
    this.global = global
    this.protocol = protocol
    this.isLoaded = false
  }

  loadScript () {
    return new Promise((resolve, reject) => {
      // Create script element and set attributes
      const script = document.createElement('script')
      script.type = 'text/javascript'
      script.async = true
      script.src = `${this.protocol}//${this.src}`

      // Append the script to the DOM
      const el = document.getElementsByTagName('script')[0]
      el.parentNode.insertBefore(script, el)

      // Resolve the promise once the script is loaded
      script.addEventListener('load', () => {
        this.isLoaded = true
        resolve(script)
      })

      // Catch any errors while loading the script
      script.addEventListener('error', () => {
        reject(new Error(`${this.src} failed to load.`))
      })
    })
  }

  load () {
    return new Promise(async (resolve, reject) => {
      if (!this.isLoaded) {
        try {
          await this.loadScript()
          resolve(window[this.global])
        } catch (e) {
          reject(e)
        }
      } else {
        resolve(window[this.global])
      }
    })
  }
}

Использование это так:

const loader = new Loader({
    src: 'cdn.segment.com/analytics.js',
    global: 'Segment',
})

// scriptToLoad will now be a reference to `window.Segment`
const scriptToLoad = await loader.load()

Вот отличное современное решение для асинхронной загрузки скрипта, хотя оно обращается только к скрипту js с асинхронным false.

На сайте www.html5rocks.com написана отличная статья - Глубокое погружение в мутные воды загрузки скриптов.

Рассмотрев множество возможных решений, автор пришел к выводу, что добавление js-скриптов в конец элемента body - лучший способ избежать блокирования рендеринга страниц при помощи js-скриптов.

Тем временем автор добавил еще одно хорошее альтернативное решение для тех людей, которые отчаянно пытаются загружать и выполнять сценарии асинхронно.

Учитывая, что вы назвали четыре сценария script1.js, script2.js, script3.js, script4.js тогда вы можете сделать это с применением async = false:

[
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.head.appendChild(script);
});

Теперь, Spec говорит: скачать вместе, выполнить по порядку, как только все загрузки.

Firefox <3.6, Opera говорит: я понятия не имею, что это за "асинхронная" вещь, но так получилось, что я выполняю сценарии, добавленные через JS, в порядке их добавления.

Safari 5.0 говорит: " Я понимаю" асинхронный ", но не понимаю, как установить" ложный "с JS. Я выполню ваши сценарии, как только они приземлятся, в любом порядке.

IE < 10 говорит: понятия "асинхронный" нет, но есть обходной путь с использованием "onreadystatechange".

Все остальное говорит: я твой друг, мы собираемся сделать это по книге.

Теперь полный код с IE < 10:

var scripts = [
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];

// Watch scripts load in IE
function stateChange() {
  // Execute as many scripts in order as we can
  var pendingScript;
  while (pendingScripts[0] && ( pendingScripts[0].readyState == 'loaded' || pendingScripts[0].readyState == 'complete' ) ) {
    pendingScript = pendingScripts.shift();
    // avoid future loading events from this script (eg, if src changes)
    pendingScript.onreadystatechange = null;
    // can't just appendChild, old IE bug if element isn't closed
    firstScript.parentNode.insertBefore(pendingScript, firstScript);
  }
}

// loop through our script urls
while (src = scripts.shift()) {
  if ('async' in firstScript) { // modern browsers
    script = document.createElement('script');
    script.async = false;
    script.src = src;
    document.head.appendChild(script);
  }
  else if (firstScript.readyState) { // IE<10
    // create a script and add it to our todo pile
    script = document.createElement('script');
    pendingScripts.push(script);
    // listen for state changes
    script.onreadystatechange = stateChange;
    // must set src AFTER adding onreadystatechange listener
    // else we’ll miss the loaded event for cached scripts
    script.src = src;
  }
  else { // fall back to defer
    document.write('<script src="' + src + '" defer></'+'script>');
  }
}

для HTML5 вы можете использовать «предварительную выборку»

<link rel="prefetch" href="/style.css" as="style" />

взгляните на «предварительную загрузку» для js.

<link rel="preload" href="used-later.js" as="script">

Я бы завершил ответ zzzzBov проверкой на наличие обратного вызова и разрешил передачу аргументов:

    function loadScript(src, callback, args) {
      var s, r, t;
      r = false;
      s = document.createElement('script');
      s.type = 'text/javascript';
      s.src = src;
      if (typeof(callback) === 'function') {
        s.onload = s.onreadystatechange = function() {
          if (!r && (!this.readyState || this.readyState === 'complete')) {
            r = true;
            callback.apply(args);
          }
        };
      };
      t = document.getElementsByTagName('script')[0];
      t.parent.insertBefore(s, t);
    }

Вот мое собственное решение для устранения JavaScript, блокирующего рендеринг:

// put all your JS files here, in correct order
const libs = {
  "jquery": "https://code.jquery.com/jquery-2.1.4.min.js",
  "bxSlider": "https://cdnjs.cloudflare.com/ajax/libs/bxslider/4.2.5/jquery.bxslider.min.js",
  "angular": "https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.2/angular.min.js",
  "ngAnimate": "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.2/angular-animate.min.js"
}
const loadedLibs = {}
let counter = 0

const loadAsync = function(lib) {
  var http = new XMLHttpRequest()
  http.open("GET", libs[lib], true)
  http.onload = () => {
    loadedLibs[lib] = http.responseText
    if (++counter == Object.keys(libs).length) startScripts()
  }
  http.send()
}

const startScripts = function() {
  for (var lib in libs) eval(loadedLibs[lib])
  console.log("allLoaded")
}

for (var lib in libs) loadAsync(lib)

Короче говоря, он загружает все ваши скрипты асинхронно, а затем выполняет их последовательно.

Репозиторий Github: https://github.com/mudroljub/js-async-loader

Я бы посоветовал вам взглянуть на Modernizr. Это небольшая облегченная библиотека, в которую вы можете асинхронно загружать свой javascript с помощью функций, которые позволяют вам проверить, загружен ли файл, и выполнить сценарий в другом указанном вами месте.

Вот пример загрузки jquery:

Modernizr.load([
  {
    load: '//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js',
    complete: function () {
      if ( !window.jQuery ) {
            Modernizr.load('js/libs/jquery-1.6.1.min.js');
      }
    }
  },
  {
    // This will wait for the fallback to load and
    // execute if it needs to.
    load: 'needs-jQuery.js'
  }
]);

Одна из причин, почему ваши скрипты могут загружаться так медленно, заключается в том, что вы запускали все свои скрипты во время загрузки страницы, например так:

callMyFunctions();

вместо:

$(window).load(function() {
      callMyFunctions();
});

Этот второй бит сценария ожидает, пока браузер полностью загрузит весь ваш код Javascript, прежде чем он начнет выполнять любой из ваших сценариев, давая пользователю понять, что страница загрузилась быстрее.

Если вы хотите улучшить взаимодействие с пользователем за счет сокращения времени загрузки, я бы не стал использовать опцию "экран загрузки". По моему мнению, это было бы гораздо более раздражающим, чем просто медленная загрузка страницы.

Вы можете найти эту статью вики интересной: https://web.archive.org/web/20150820074329/http://ajaxpatterns.org:80/On-Demand_Javascript

Это объясняет, как и когда использовать такую ​​технику.

Рассматривали ли вы использовать Fetch Injection? Я свернул библиотеку с открытым исходным кодом, названную fetch-inject, для обработки подобных случаев. Вот как может выглядеть ваш загрузчик при использовании lib:

fetcInject([
  'js/jquery-1.6.2.min.js',
  'js/marquee.js',
  'css/marquee.css',
  'css/custom-theme/jquery-ui-1.8.16.custom.css',
  'css/main.css'
]).then(() => {
  'js/jquery-ui-1.8.16.custom.min.js',
  'js/farinspace/jquery.imgpreload.min.js'
})

Для обратной совместимости используйте функцию обнаружения и отката к элементам XHR Injection или Script DOM или просто вставьте теги на страницу, используя document.write,

Вот небольшая функция ES6, если кто-то хочет использовать ее, например, в React

import {uniqueId} from 'lodash' // optional
/**
 * @param {String} file The path of the file you want to load.
 * @param {Function} callback (optional) The function to call when the script loads.
 * @param {String} id (optional) The unique id of the file you want to load.
 */
export const loadAsyncScript = (file, callback, id) => {
  const d = document
  if (!id) { id = uniqueId('async_script') } // optional
  if (!d.getElementById(id)) {
    const tag = 'script'
    let newScript = d.createElement(tag)
    let firstScript = d.getElementsByTagName(tag)[0]
    newScript.id = id
    newScript.async = true
    newScript.src = file
    if (callback) {
      // IE support
      newScript.onreadystatechange = () => {
        if (newScript.readyState === 'loaded' || newScript.readyState === 'complete') {
          newScript.onreadystatechange = null
          callback(file)
        }
      }
      // Other (non-IE) browsers support
      newScript.onload = () => {
        callback(file)
      }
    }
    firstScript.parentNode.insertBefore(newScript, firstScript)
  } else {
    console.error(`The script with id ${id} is already loaded`)
  }
}

Краткий ответ, пояснения в коде

      function loadScript(src) {
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
//
// script.async = false; // uncomment this line and scripts will be executed in the document order, like 'defer' option
//
// script.defer = true; // uncomment this line when scripts need whole DOM and/or relative order execution is important
//
// the script starts loading as it's append to the document and dynamic script behave as “async” by default
// other scripts don’t wait for 'async' scripts, and 'async' scripts don’t wait for them
// scripts that loads first – runs first (“load-first” order)
document.body.append(script); 
}

loadScript('js/example01.js');
loadScript('js/example02.js');

/* 
the 'defer' attribute tells the browser not to wait for the script
the 'async' attribute make script to load in the background and run when ready
the 'async' and 'defer' attribute are only for external scripts
'defer' is used for scripts that need the whole DOM and/or their relative execution order is important
'async' is used for independent scripts, like counters or ads, when their relative execution order does not matter
More: https://javascript.info/script-async-defer
*/

Что ж, x.parentNode возвращает элемент HEAD, поэтому вы вставляете скрипт непосредственно перед тегом head. Может в этом проблема.

Пытаться x.parentNode.appendChild() вместо.

Проверьте это https://github.com/stephen-lazarionok/async-resource-loader. В нем есть пример, который показывает, как загрузить JS, CSS и несколько файлов одним выстрелом.

      const dynamicScriptLoading = async (src) =>
{
    const response = await fetch(src);
    const dataResponse = await response.text();
    eval.apply(null, [dataResponse]);
}

Вы можете использовать LABJS или RequreJS

Загрузчики скриптов, такие как LABJS, RequireJS улучшит скорость и качество вашего кода.

Я бы посоветовал сначала изучить вопрос минимизации файлов и посмотреть, даст ли это достаточно большой прирост скорости. Если ваш хост работает медленно, попробуйте поместить этот статический контент в CDN.

Другие вопросы по тегам