Загрузите и выполните код JavaScript СИНХРОННО - PullRequest
36 голосов
/ 20 мая 2011

Есть ли способ загрузить и выполнить файл JavaScript синхронным способом, как синхронный запрос XMLHttpRequest?

В настоящее время я использую синхронизацию XMLHttpRequest, а затем eval для этого, но отладка этого кода очень сложна ...

Спасибо за вашу помощь!

Обновление

Я попробовал это сейчас:

test.html

<html>
    <head>
        <script type="text/javascript">
            var s = document.createElement("script");
            s.setAttribute("src","script.js");
            document.head.appendChild(s);
            console.log("done");
        </script>
    </head>
    <body>
    </body>
</html>

script.js

console.log("Hi");

Выход: сделанный Привет

Так что это не было выполнено синхронно. Любая идея, чтобы «Привет» появился первым?

Обновление 2 Другой пример

test.html (код внутри тега скрипта)

var s = document.createElement("script");
s.setAttribute("src","script.js");
document.head.appendChild(s);
SayHi();

script.js

function SayHi(){
    console.log("hi");
}

Вывод: Uncaught ReferenceError: SayHi не определено

Ответы [ 5 ]

20 голосов
/ 04 февраля 2014

Если вы используете это:

function loadScriptSync (src) {
    var s = document.createElement('script');
    s.src = src;
    s.type = "text/javascript";
    s.async = false;                                 // <-- this is important
    document.getElementsByTagName('head')[0].appendChild(s);
}

Вы можете делать то, что вы хотите (хотя разделены в дополнительном файле сценария)

test.html (кодвнутри тега скрипта):

loadScriptSync("script.js");
loadScriptSync("sayhi.js"); // you have to put the invocation into another script file

script.js :

function SayHi() {
     console.log("hi");
}

sayhi.js :

SayHi();
16 голосов
/ 20 мая 2011

Все сценарии, которые загружаются после готовности DOM, загружаются асинхронно.Единственной причиной, по которой браузер загружает их синхронно, является функция write, которая может что-то выводить.Таким образом, вы можете использовать обратный вызов onload элемента script для достижения желаемого.

var s = document.createElement("script");
s.setAttribute("src","script.js");
s.onload = function(){
    console.log('Done');
}
document.head.appendChild(s);

Другой способ - загрузить js-файл через XHR и установить код внутри элемента script:

window.onload = function(){
    var req = new XMLHttpRequest();
    req.open('GET', "test.js", false);
    req.onreadystatechange = function(){
        if (req.readyState == 4) {
            var s = document.createElement("script");
            s.appendChild(document.createTextNode(req.responseText));
            document.head.appendChild(s);
        }
    };
    req.send(null);
}
12 голосов
/ 31 мая 2012

Из аналогичного вопроса (https://stackoverflow.com/a/3292763/235179):

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"><\/script>');
</script>

<script type="text/javascript">
  functionFromOther();
</script>

Либо код, вызываемый из сценария document.write ', должен быть в своем собственном <script>, либо в событии window.onload().

5 голосов
/ 20 мая 2011

Ваши сценарии выполняются синхронно

Ваш код, если сложить это:

1. create script element
2. set its attribute src
3. set its attribute deferred
4. display done...

эта первая часть останавливает выполнение и передает его следующему сценарию

5. script executes and displays Hi

Все очень синхронно ... В Javascript некоторый код выполняется полностью, пока не выполнится до последней строки или не передаст выполнение внутренним системам (таким как XHR или таймер).

Когда кто-то хочет подготовить некоторые части для последующего выполнения, они готовят его с помощью setTimeout. Даже если тайм-аут короче, чем остальная часть кода, это займет время, которое он выполнит. После завершения выполнения кода. Пример:

// some code
setTimeout(function(){ alert("I'm second alert"); }, 1);
longExecutionTask();
alert("I'm the first alert");

В приведенном выше коде, даже если setTimeout настроен на выполнение после 1 мс, он не запустится до тех пор, пока код не завершит выполнение, которое заканчивается отображением поля alert. То же самое происходит в вашем случае. Первая партия кода должна завершиться, прежде чем что-либо еще может запуститься.

Почему вы получаете исключение (в примере 2)

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

Добавление тега скрипта не приведет к его немедленному исполнению. Загрузка скрипта + выполнение произойдет, когда парсер HTML попадет к добавленному элементу SCRIPT. Он загрузит его в этот момент и оценит / выполнит его содержимое.

  1. HTML-анализатор начинает анализ вашего документа
  2. HEAD анализируется, а дочерний тег SCRIPT анализируется и выполняется. Это выполнение добавляет еще один элемент к тегу BODY, который еще не проанализирован.
  3. Parser переходит к BODY и анализирует его содержимое (недавно добавленный тег SCRIPT), который затем загружает скрипт и выполняет его содержимое.

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

0 голосов
/ 14 января 2018

Вы можете синхронизировать асинхронные операции между собой.Создайте рекурсивную функцию для представления цикла и вызовите следующую операцию, когда закончите предыдущий.Следующая функция импортирует скрипты в порядке аналогичной техники.Он ожидает загрузки скрипта и, если ошибки нет, переходит к следующему.Если возникает ошибка, она вызывает функцию обратного вызова с событием ошибки, а если ошибки нет - вызывает ту же функцию обратного вызова с нулем после загрузки всех сценариев.Он также очищает после себя с s.parentNode.removeChild(s).

function importScripts(scripts, callback) {
    if (scripts.length === 0) {
        if (callback !== undefined) {
            callback(null);
        }
    }
    var i = 0, s, r, e, l;
    e = function(event) {
        s.parentNode.removeChild(s);
        if (callback !== undefined) {
            callback(event);
        }
    };
    l = function() {
        s.parentNode.removeChild(s);
        i++;
        if (i < scripts.length) {
            r();
            return;
        }
        if (callback !== undefined) {
            callback(null);
        }
    };
    r = function() {
        s = document.createElement("script");
        s.src = scripts[i];
        s.onerror = e;
        s.onload = l;
        document.head.appendChild(s);
    };
    r();
}
...