Неправильный порядок выполнения с использованием LABjs

Я сделал очень простой пример (адаптированный из моего реального проекта), который использует LABjs (v2.0.3) для загрузки файлов javascript и выполнения их в заданном порядке. Я вставляю код ниже.

  • Поскольку testLAB.js ожидает mainCanvas.js, который ожидает events.js, я ожидаю, что порядок предупреждений будет следующим: "events.js" "mainCanvas.js" "testLAB.js".
  • Однако обычно я получаю либо в обратном порядке: "testLAB.js", "mainCanvas.js", "events.js".
  • Иногда я получаю "testLAB.js", "events.js", "mainCanvas.js".

Кто-нибудь может объяснить?
Полный пример можно скачать здесь.

РЕДАКТИРОВАТЬ: я использую node.js и модуль http-сервера для локального обслуживания этих страниц (на случай, если вы хотите попробовать его локально)

файл: index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Test lab.js</title>
    <script src="js/lib/LAB.js"></script>
    <script src="js/app/testLAB.js"></script>
</head>
<body>
</body>
</html>

файл: js/app/testLAB.js

$LAB.setGlobalDefaults({
    BasePath: "/js/",
    Debug:true
});

$LAB
.script("lib/underscore.min.js")
.script("app/mainCanvas.js").wait(function(){
    alert("testLAB.js");
});

файл: js/app/mainCanvas.js

$LAB
.script("lib/jquery.js").wait()
.script("lib/underscore.min.js")
.script("app/events.js")
.wait(function() {
    alert("mainCanvas.js");
});

файл: js/app/events.js

$LAB
.script("lib/jquery.js")
.script("lib/underscore.min.js")
.wait(function() {
alert("events.js");
});

1 ответ

Решение

Проблема здесь заключается в неправильном понимании того, как работает LABjs, или, скорее, как работает вложенная динамическая загрузка скриптов.

Если я загружаю скрипт A.js с помощью загрузчика скриптов, браузер загружает и затем выполняет содержимое этого скрипта, а внутри скрипта A я использую загрузчик скриптов для загрузки скрипта B.js, тот факт, что скрипт A.js Запрос на загрузку B.js не означает, что событие "load" A.js магически блокируется до тех пор, пока зависимости (то есть, в данном случае, B.js) не завершат загрузку и выполнение.

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

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


Другой (более простой?) Вариант, который я лично делаю, если вы не хотите идти по пути AMD/Require, - это использовать простой инструмент для сборки, который читает ваши файлы JS во время сборки, обнаруживает все вложенные зависимости находятся и "поднимают" их в правильном порядке в один "глобальный" цепной вызов $LAB. Поэтому внутри ваших файлов вместо объявления вложенных зависимостей с помощью реальных вызовов $LAB просто укажите обозначения зависимостей, как в комментарии JS, и пусть ваш скрипт их ищет.

Пример (аналогично тому, как я делаю вещи сам):

A.js:

// needs "B.js"
// needs "C.js"

A.something = function( /*..*/ ) { /*..*/ };    
B.something();
C.something();

B.js:

// needs "D.js"

B.something = function( /*..*/ ) { /*..*/ };
D.something();

C.js:

// needs "D.js"
// needs "E.js"

C.something = function( /*..*/ ) { /*..*/ };
D.something();
E.something();

D.js:

D.something = function( /*..*/ ) { /*..*/ };

E.js:

E.something = function( /*..*/ ) { /*..*/ };

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

Затем он создает единственную цепочку $LAB для использования на моей главной странице, которая выглядит следующим образом:

$LAB
.script("D.js")
.script("E.js").wait() // D and E can execute in either order
.script("B.js")
.script("C.js").wait() // B and C can execute in either order
.script("A.js")
.wait(function(){
   alert("Everything is loaded!");
});

Единственное предостережение к подходу, который я предлагаю, заключается в том, что вы не можете делать циклические зависимости (например, C зависит от B, а B зависит от C). Если вам нужны циклические зависимости, перейдите в AMD/Require.js.

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