Как динамически загружать несколько скриптов в определенном порядке?

Мне нужно динамически загружать x скриптов (скажем, 6 для этого примера) (либо в TypeScript, либо в JavaScript).

Первые 2 сценария используются последними 4. Поэтому я должен убедиться, что сценарии 1 и 2 загружены до загрузки последних 4.

var scripts = [
  { "path": "f1.js", "isDependency": true },
  { "path": "f2.js", "isDependency": true },
  { "path": "f3.js", "isDependency": false },
  { "path": "f4.js", "isDependency": false },
  { "path": "f5.js", "isDependency": false },
  { "path": "f6.js", "isDependency": false }
];

И у меня есть асинхронная функция, которая загружает скрипты, но у меня возникают проблемы с настройкой async/await или обещаний, которые ждут загрузки первых 2 скриптов, а затем позволяют другим загружаться в любом порядке.

private async loadJsFileAsync(filePath: string) {
  return new Promise((resolve, reject) => {
    if (!document.getElementById(filePath)) {
      var script = document.createElement('script');

      script.onload = function () {
        // mark with attribute so we know that it's loaded
        script.attributes["data-loaded"] = "true";
        resolve();
      }
      script.onerror = function (err) {
        reject(err);
      }

      script.type = "text/javascript";
      script.src = filePath
      script.id = filePath;
      document.getElementsByTagName('head')[0].appendChild(script);

    } else {
      // file processed, but check to see it actually loaded
      var s1 = <HTMLScriptElement>document.getElementById(filePath);
      var loadedAttribute = s1.attributes["data-loaded"] === "true";
      if (loadedAttribute) {
        resolve();
      } else {

        // add event listeners (in addition to the existing ones)
        s1.addEventListener("load", function () {
          resolve();
        });
        s1.addEventListener("error", function (err) {
          reject(err);
        });
      }
    }
  });
}

На данный момент я использую асинхронную функцию только с одним необходимым скриптом следующим образом:

this.loadJsFileAsync("f1.js").then(value => {

  // load other files
  loadJsFile("f3.js");
  loadJsFile("f4.js");
  loadJsFile("f5.js");
  loadJsFile("f6.js");
});

private loadJsFile(filePath: string) {
  var script = document.createElement('script');
  script.type = "text/javascript";
  script.src = filePath + this.version();
  script.id = filePath;
  document.getElementsByTagName('head')[0].appendChild(script);
}   

Можете ли вы опубликовать свое использование функции? (Как вы используете ожидание)

mwilson 10.04.2019 01:03

@mwilson Сейчас я просто использую эту функцию с одним необходимым скриптом. Опубликовано.

AngryHacker 10.04.2019 01:07
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
2
878
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Прежде всего, почему бы не использовать requirejs или какой-либо другой загрузчик модулей? Они предоставляют вам возможность связывать ваши скрипты, чтобы зависимости загружались первыми. Во-вторых, я думаю, это то, что вы хотите:

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

class Script {
    constructor(path, dependencyOrder) {
        this.path = path;
        this.dependencyOrder = dependencyOrder;
    }
}
class ScriptLoader {
    constructor() {
        this.scripts = [];

    }
    addScript(script) {
        const exists = this.scripts.find( s => s.path.toLowerCase() === script.path.toLowerCase());
        if (!exists) {
            this.scripts.push(script);
        }
    }
    async loadScripts() {
        if (Array.isArray(this.scripts)) {
            const orderedScripts = this.orderScriptsByDependency();
            let scriptsLoaded = false;
            let counter = 0;
            while (!scriptsLoaded) {
                const _script = orderedScripts[counter]
                await this.loadScript(_script, _script.dependencyOrder > -1);
                counter += 1;
                scriptsLoaded = counter === orderedScripts.length;
            }
        }
    }
    async loadScript(script, waitToLoad) {
        return new Promise( (resolve, reject) => {
            const scriptTag = document.createElement('script');
            scriptTag.src = script.path;
            if (waitToLoad) {
                scriptTag.async = true;
                document.body.appendChild(scriptTag);
                scriptTag.onload = () => { resolve(); }
            } else {
                document.body.appendChild(scriptTag);
                resolve();
            }
        } );
    }
    orderScriptsByDependency() {
        if (Array.isArray(this.scripts)) {
            return this.scripts.sort( (a, b) => {
                return a.dependencyOrder - b.dependencyOrder;
            });
        }
        return null;
    }
}

Применение

class IndexView {
    constructor() {
        this.loader = new ScriptLoader();
        this.init();
    }
    async init() {
        this.loader.addScript(new Script('scripts/script0.js', 0));
        this.loader.addScript(new Script('scripts/script1.js', 1));
        this.loader.addScript(new Script('scripts/script2.js', 2));
        await this.loader.loadScripts();
    }
}

Requirejs

ой. Забыл вернуть значение в моей функции сортировки. Обновление сейчас

mwilson 10.04.2019 01:47

Если вы хотите сохранить порядок, вы можете использовать onload из HtmlScriptElement вот так.

const loadScripts = (urls: string[]): void => {
  urls.forEach((url, index) => {
    const js = loadScript(url);
    if (index < urls.length) {
      js.onload = () => {
        loadScript(urls[index + 1]);
      };
    }
  });
};

const loadScript = (url: string): HTMLScriptElement => {
  const js = document.createElement("script");
  js.setAttribute("src", url);
  document.body.appendChild(js);
  return js;
};

И назовите это так:

loadScripts(["script1.js", "script2.js", "script3.js"]);

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

mwilson 10.04.2019 02:31

Да, ваше решение лучше с асинхронной загрузкой.

Hiral Desai 11.04.2019 01:30

@mwilson Я добавил ответ с функцией jQuery, которая загружает скрипты, не связывая их.

Eugene 05.11.2020 16:32

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