Часть JavaScript выполнена или не выполнена, если предыдущий script не работает
Я только что узнал важный факт о выполнении javascript в случае возникновения ошибки. Прежде чем я начну делать выводы из этого, я лучше проверю, прав ли я.
Учитывая HTML-страницу, включая 2 скрипта:
<script src="script1.js" />
<script src="script2.js" />
script1:
doSomething();
Скрипт2:
doSomeOtherThing();
Это приводит к тому, что один script обрабатывается как единое целое:
doSomething();
doSomeOtherThing();
В частности, если doSomething
выдает ошибку, выполнение прерывается. "script2" никогда не выполняется.
Это мой "Урок 1" - можно подумать, так как это отдельный файл, на который он не влияет. Но это так. = > см. "последнее обновление" ниже
Теперь, если мы изменим script2 следующим образом (предположим, что мы включили jQuery где-то выше):
$(document).ready({ doSomeOtherThing(); });
и поместите script перед скриптом2:
<script src="script2.js" />
<script src="script1.js" />
Порядок выполнения по-прежнему остается "doSomething()" (иногда) "doSomeOtherThing()".
Однако он выполняется в двух "единицах":
-
doSomething
выполняется в начале документа java script
-
doSomeOtherThing
выполняется при обработке события document.ready.
Если doSomeOtherThing
выдает исключение, он будет не разорвать вторую часть обработки.
(Я воздержусь от использования термина thread
, потому что считаю, что все script обычно выполняются одним и тем же потоком, или, точнее, это может зависеть от браузера.)
Итак, моя Lession 2: хотя ошибка JavaScript может помешать выполнению любых последующих скриптов, она не останавливает цикл событий.
Заключение 1
$(document).ready()
отлично справляется с определением фрагментов кода JavaScript, которые должны выполняться независимо от любых других исполняемых скриптов.
Или, другими словами: если у вас есть фрагмент JavaScript и вы хотите убедиться, что он выполняется, даже если другие скрипты не работают, поместите его в $(document).ready()
.
Это было бы ново для меня, потому что я использовал бы только событие, если script зависит от полностью загруженного документа.
Заключение 2
Сделав еще один шаг, было бы хорошим решением архитектуры обернуть все сценарии в $(document).ready()
, чтобы убедиться, что все сценарии "поставлены в очередь" для выполнения. Во втором примере выше, если script2.js
был включен после script1.js
, как в примере 1:
<script src="script1.js" />
<script src="script2.js" />
Ошибка в script1.js помешает doSomeOtherThing()
даже зарегистрироваться, потому что функция $(document).ready()
не будет выполнена.
Однако, если script1.js использовал $(document).ready()
тоже, этого не произошло бы:
$(document).ready(function() { doSomething(); });
$(document).ready(function() { doSomeOtherThing(); });
Обе строки будут выполнены. Затем цикл событий выполнил бы doSomething
, который сломался бы, но doSomeOtherThing
не будет затронут.
Еще одна причина для этого состоит в том, что поток, отображающий страницу, может как можно скорее вернуться, а цикл события может использоваться для запуска выполнения кода.
Критика/Вопросы:
- Я ошибался?
- Какие существуют причины, из-за которых необходимо немедленно выполнить часть кода, т.е. не обернуть ее в событие?
- Будет ли это сильно влиять на производительность?
- Есть ли другой/лучший способ достичь того же, а не использовать событие готовности документа?
- Можно ли определить порядок выполнения скриптов, если все сценарии просто регистрируют свой код как обработчик событий? Выполняются ли обработчики событий в том порядке, в котором они были зарегистрированы?
С нетерпением ждем любых полезных комментариев!
Позднее обновление:
Как правильно указал Briguy37, мое наблюдение, должно быть, было неправильным в первую очередь. ( "Я ошибался - да!" ). Взяв его простой пример, я могу воспроизвести, что во всех основных браузерах и даже в IE8 скрипт2 выполняется, даже если скрипт1 выдает ошибку.
Все еще @Marcello большой ответ помогает получить представление о концепциях стеков исполнения и т.д. Кажется, что каждый из двух сценариев выполняется в отдельном стеке выполнения.
Ответы
Ответ 1
Ваше первое предположение, что они работают как один script, неверно. Script2 будет выполняться, даже если Script1 выдает ошибку. Для простого теста выполните следующую структуру файлов:
-anyFolder
--test.html
--test.js
--test2.js
Содержимое test.html:
<html>
<head>
<script type="text/javascript" src="test.js"></script>
<script type="text/javascript" src="test2.js"></script>
</head>
</html>
Содержимое test.js:
console.log('test before');
throw('foo');
console.log('test after');
Содержимое test2.js:
console.log('test 2');
Вывод при открытии test.html(в консоли):
test before test.js:1
Uncaught foo test.js:2
test 2
Из этого теста вы можете видеть, что test2.js все еще работает, хотя test.js выдает ошибку. Тем не менее, test.js прекращает выполнение после того, как он запустится в ошибку.
Ответ 2
Способ JS обрабатывает ошибки, зависит от того, как JS обрабатывает script. Он не имеет ничего (или мало), чтобы делать с потоками. Поэтому вам нужно сначала подумать о том, как JS работает через ваш код.
Прежде всего, JS будет читать в каждом файле script файл/блок последовательно (в этот момент ваш код просто рассматривается как текст).
Затем JS начинает интерпретировать эти текстовые блоки и компилирует их в исполняемый код. Если обнаружена синтаксическая ошибка, JS прекращает компиляцию и переходит к следующему script. В этом процессе JS обрабатывает каждый script блоки/файлы как отдельный объект, поэтому синтаксические ошибки в script 1 не обязательно нарушают выполнение script 2. Код будет интерпретироваться и скомпилирован, но не выполнен в этот пункт, поэтому команда throw new Error
не нарушит выполнение.
После того, как все script файлы/блоки скомпилированы, JS проходит через код (начиная с первого файла кода/блока в вашем коде) и создавая так называемый исполняемый стек (функция функция звонков b вызывает функцию d и c....) и выполнить его в заданном порядке. Если в какой-то момент происходит ошибка обработки или выбрана программно (throw new Error('fail')
), все выполнение этого стека прекращается, и JS возвращается к началу в начало этого стека и начинает с выполнения следующего возможного стека.
Тем не менее, причина, по которой ваша функция onload по-прежнему выполняется после ошибки в script1.js, не происходит из-за нового потока или чего-то еще, это просто потому, что событие создает отдельный стек выполнения, JS может перейти к, после того, как произошла ошибка в предыдущем стеке выполнения.
Приступая к вашим вопросам:
Какие существуют причины, из-за которых необходимо немедленно выполнить часть кода, т.е. не обернуть ее в событие?
Я бы посоветовал вам не иметь "незамедлительно" код в вашем веб-приложении. Лучшей практикой является наличие одной точки входа в ваше приложение, которая вызывается внутри события onload
$(document).ready(function () {App.init()});
Это, однако, не имеет никакого отношения к обработке ошибок или тому подобное. Сама обработка ошибок должна обязательно выполняться внутри вашего кода с помощью условных выражений if(typeof myValue !== 'undefined')
или блоков try/catch/finally, где вы можете ожидать потенциальных ошибок. Это также дает вам возможность попробовать второй способ внутри блока catch или изящно обработать ошибку в конце.
Если вы можете создать приложение, основанное на событиях (конечно, не для обработки ошибок), сделайте это. JS - это язык, управляемый событиями, и вы можете максимально использовать его, когда записываете управляемый событиями код...
Будет ли это сильно влиять на производительность?
Подход, основанный на событиях, заставил бы IMHO сделать ваше приложение еще лучше и сделать его более прочным одновременно. Управляемый событиями код может помочь вам уменьшить количество внутренней логики обработки, вам просто нужно войти в нее.
Есть ли другой/лучший способ достичь того же, а не использовать событие готовности документа?
Как упоминалось ранее: try/catch/finally
Можно ли определить порядок выполнения скриптов, если все сценарии просто регистрируют свой код как обработчик событий? Выполняются ли обработчики событий в том порядке, в котором они были зарегистрированы?
Если вы регистрируете одно и то же событие на одном и том же объекте, порядок сохраняется.
Ответ 3
Я не уверен в синтаксических ошибках, но вы можете использовать try {} catch(e) {}
, чтобы поймать ошибки и сохранить остальную часть кода.
НЕ запускается до конца
var json = '{"name:"John"'; // Notice the missing curly bracket }
// This probably will throw an error, if the string is buggy
var obj = JSON.parse(json);
alert('This will NOT run');
Будет выполняться до конца
var json = '{"name:"John"'; // Notice the missing curly bracket }
// But like this you can catch errors
try {
var obj = JSON.parse(json);
} catch (e) {
// Do or don'
}
alert('This will run');
UPDATE
Я просто хотел показать, как убедиться, что остальная часть кода будет выполнена в случае возникновения ошибки.
Какие существуют причины, необходимые для выполнения части код немедленно, то есть не обертывать его в событие?
Производительность. Каждое такое событие заполняет очередь событий. Не то, чтобы это сильно повредило, но это просто не нужно... Зачем делать некоторые работы позже, если это можно сделать прямо сейчас? Например - обнаружение браузера и прочее.
Будет ли это сильно влиять на производительность?
Если вы делаете это много раз в секунду, тогда да.
Есть ли другой/лучший способ добиться того же, а не использовать готовое событие документа?
Да. См. Пример выше.
Можно ли определить порядок выполнения скриптов, если все сценарии просто зарегистрировать свой код как обработчик событий? Являются ли обработчики событий выполненных в том порядке, в котором они были зарегистрированы?
Да, я уверен, что они есть.