JavaScript гарантированно будет однопоточным? - PullRequest
574 голосов
/ 29 апреля 2010

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

Ответы [ 12 ]

550 голосов
/ 29 апреля 2010

Хороший вопрос. Я хотел бы сказать «да». Я не могу.

Считается, что JavaScript обычно имеет один поток выполнения, видимый для сценариев (*), поэтому при вводе встроенного сценария, прослушивателя событий или тайм-аута вы будете полностью контролировать, пока не вернетесь с конца своего блока или функция.

(*: игнорирование вопроса о том, действительно ли браузеры реализуют свои движки JS, используя один поток ОС, или WebWorkers вводят другие ограниченные потоки выполнения.)

Однако, в действительности это не совсем верно , в подлых противных отношениях.

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

var l= document.getElementById('log');
var i= document.getElementById('inp');
i.onblur= function() {
    l.value+= 'blur\n';
};
setTimeout(function() {
    l.value+= 'log in\n';
    l.focus();
    l.value+= 'log out\n';
}, 100);
i.focus();
<textarea id="log" rows="20" cols="40"></textarea>
<input id="inp">

Результаты log in, blur, log out на всех, кроме IE. Эти события не просто запускаются, потому что вы вызвали focus() напрямую, они могут произойти, потому что вы вызвали alert(), или открыли всплывающее окно, или что-нибудь еще, что перемещает фокус.

Это также может привести к другим событиям. Например, добавьте прослушиватель i.onchange и введите что-нибудь во входные данные до того, как вызов focus() разфокусирует его, и порядок записи будет log in, change, blur, log out, за исключением Opera, где он log in, blur, log out, change, и IE, где это (даже менее объяснимо) log in, change, log out, blur.

Аналогичным образом вызов click() для элемента, который его предоставляет, вызывает обработчик onclick сразу во всех браузерах (по крайней мере, это соответствует!).

(здесь я использую свойства прямого обработчика событий on..., но то же самое происходит с addEventListener и attachEvent.)

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

var l= document.getElementById('log');
document.getElementById('act').onclick= function() {
    l.value+= 'alert in\n';
    alert('alert!');
    l.value+= 'alert out\n';
};
window.onresize= function() {
    l.value+= 'resize\n';
};
<textarea id="log" rows="20" cols="40"></textarea>
<button id="act">alert</button>

Нажмите alert, и вы получите модальное диалоговое окно. Больше сценарий не выполняется, пока вы не закроете этот диалог, да? Нету. Измените размер главного окна, и вы получите alert in, resize, alert out в текстовой области.

Вы можете подумать, что невозможно изменить размер окна, пока модальное диалоговое окно открыто, но это не так: в Linux вы можете изменить размер окна так, как вам нравится; в Windows это не так просто, но вы можете сделать это, изменив разрешение экрана с большего на меньшее, где окно не помещается, что приводит к изменению его размера.

Вы можете подумать, что только resize (и, возможно, еще несколько, как scroll) могут срабатывать, когда пользователь не имеет активного взаимодействия с браузером, потому что сценарий является многопоточным. И для одиночных окон вы можете быть правы. Но это все подходит к концу, как только вы выполняете межоконный сценарий. Для всех браузеров, кроме Safari, который блокирует все окна / вкладки / фреймы, когда какой-либо из них занят, вы можете взаимодействовать с документом из кода другого документа, работая в отдельном потоке выполнения и вызывая любые связанные обработчики событий для огонь.

Места, где могут быть сгенерированы события, которые вы можете вызвать, пока сценарий все еще находится в потоке:

  • когда модальные всплывающие окна (alert, confirm, prompt) открыты во всех браузерах, кроме Opera;

  • во время showModalDialog в браузерах, которые его поддерживают;

  • диалоговое окно «Сценарий на этой странице может быть занят ...», даже если вы решите позволить сценарию продолжать работу, позволяет запускать такие события, как изменение размера и размытие, и обрабатывать их даже во время выполнения сценария. находится в середине занятого цикла, кроме Opera.

  • Некоторое время назад для меня, в IE с плагином Sun Java, вызов любого метода в апплете мог позволить запускать события и повторно вводить сценарий. Это всегда была ошибка, чувствительная ко времени, и, возможно, Sun исправила ее с тех пор (я, конечно, надеюсь).

  • наверное больше. Прошло много времени с тех пор, как я протестировал это, и с тех пор браузеры стали более сложными.

Таким образом, для большинства пользователей JavaScript, как правило, имеет строгий поток выполнения, управляемый событиями. На самом деле такого не бывает. Непонятно, насколько это просто ошибка, а какой умышленный дизайн, но если вы пишете сложные приложения, особенно с кросс-оконными / фрейм-скриптовыми сценариями, есть все шансы, что они могут вас укусить - и периодически, трудные для отладки способы.

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

111 голосов
/ 29 апреля 2010

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

Добавьте к этому тот факт, что HTML5 уже определяет Web Workers (явный, стандартизированный API для многопоточного кода javascript), вводящий многопоточность в базовый Javascript, будет в основном бессмысленным.

( Примечание для других комментаторов: Несмотря на то, что setTimeout/setInterval, события загрузки HTTP-запроса (XHR) и события пользовательского интерфейса (щелчок, фокус и т. Д.) Дают общее представление о многопоточности - все они по-прежнему выполняются по одной временной шкале - по одному за раз - поэтому даже если мы заранее не знаем их порядок выполнения, нет необходимости беспокоиться об изменении внешних условий во время выполнения обработчика событий, функции времени или обратного вызова XHR. .)

16 голосов
/ 29 апреля 2010

Да, хотя при использовании любого из асинхронных API-интерфейсов, таких как setInterval и xmlhttp callbacks, все еще могут возникать некоторые проблемы параллельного программирования (в основном, состояния гонки).

10 голосов
/ 29 апреля 2010

Да, хотя Internet Explorer 9 скомпилирует ваш Javascript в отдельном потоке для подготовки к выполнению в основном потоке. Это ничего не изменит для вас как для программиста.

7 голосов
/ 29 апреля 2010

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

7 голосов
/ 29 апреля 2010

JavaScript / ECMAScript предназначен для жизни в среде хоста. То есть JavaScript на самом деле ничего не делает , если только хост-среда не решит проанализировать и выполнить данный сценарий и не предоставит объекты среды, которые позволяют JavaScript на самом деле быть полезным (например, DOM в браузерах). *

Я думаю, что данная функция или блок скрипта будут выполняться построчно, и это гарантировано для JavaScript. Однако, возможно, среда хоста может выполнять несколько сценариев одновременно. Или среда хоста всегда может предоставить объект, который обеспечивает многопоточность. setTimeout и setInterval являются примерами или, по крайней мере, псевдо-примерами среды хоста, обеспечивающей способ выполнения некоторого параллелизма (даже если это не совсем параллелизм).

5 голосов
/ 12 декабря 2016

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

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

Nodejs следовал подходу браузеров.

Rhino engine, однако, поддерживает запуск кода js в разных потоках . Выполнения не могут совместно использовать контекст, но они могут совместно использовать область. Для этого конкретного случая в документации указано:

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

Из прочтения документации Rhino я заключаю, что кто-то может написать javascript api, который также порождает новые потоки javascript, но api будет специфичным для rhino (например, node может порождать только новый процесс).

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

То, что я вижу в браузерах и nodejs:

  • Все ли js-коды выполняются в одном потоке? : Да.

  • Может ли js-код запускать другие потоки? : Да.

  • Могут ли эти потоки изменить контекст выполнения js? Нет. Но они могут (прямо / косвенно (?)) Добавлять в очередь событий.

Так, в случае браузеров и nodejs (и, вероятно, многих других движков) javascript не является многопоточным, но сами движки являются.

4 голосов
/ 26 мая 2016

@ Bobince дает действительно непрозрачный ответ.

Отстраняясь от ответа Мара Орлигссона, Javascript всегда однопоточен из-за этого простого факта: все в Javascript выполняется по одной временной шкале.

Это строгое определение однопоточного языка программирования.

4 голосов
/ 13 февраля 2012

номер

Я иду против толпы здесь, но терпите меня. Предполагается, что один JS-скрипт будет эффективно однопоточным, но это не означает, что его нельзя интерпретировать по-разному.

Допустим, у вас есть следующий код ...

var list = [];
for (var i = 0; i < 10000; i++) {
  list[i] = i * i;
}

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

Первая тема

for (var i = 0; i < 5000; i++) {
  list[i] = i * i;
}

Вторая нить

for (var i = 5000; i < 10000; i++) {
  list[i] = i * i;
}

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

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

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

// like "use strict" this enables certain features on compatible VMs.
"use parallel";

var list = [];

// This string, which has no effect on incompatible VMs, enables threading on
// this loop.
"parallel for";
for (var i = 0; i < 10000; i++) {
  list[i] = i * i;
}

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

3 голосов
/ 29 апреля 2010

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

Нет никакой поддержки в Javascript для многопоточности, по крайней мере, явно, так что это не имеет значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...