
Да, в JavaScript тоже есть многопоточность(либо подобие) и реализована по средством инструмента Web Workers, о использовании которого и поговорим в данном посте.
Идея реализации: “тяжелые” операции, которые можно выполнять асинхронно(не блокирую работу браузера), выносятся в отдельные модули-файлы; это может быть: обработка большого количества данных, кеширование, проверка правописания, фильтрация изображений на canvas. В браузере для каждого такого файла создается объект Worker, по средством которого и осуществляется коммуникация.
Другими словами: используя технологию Web Workers можно избежать появления вот этого сообщения

Проверка поддержки браузером
Для проверки поддерживает ли браузер технологию Web Workers достаточно проверить наличие объекта window.Worker:
if (!!window.Worker)
{
//поддерживается
}
Простой пример
На сайте w3 дали довольно наглядный пример, который я немного модифицировал расширив передачу данных в обе стороны. Итак, у нас есть 2 файла: основной(для простоты пишем внутри хтмл файла) test.html и файл воркера worker.js. Мы задались целью выводить на экран простые числа в заданном диапозоне, но, так как операция по определению является ли число простым довольно ресурсоемкая, особенно для больших чисел, мы вынесем ее в отдельный фоновый поток (worker). Код этой функции и будет находиться в файле worker.js.
Итого, test.html:
<!DOCTYPE HTML>
<html>
<body>
<output id="result"></output>
<script>
var worker = new Worker('worker.js');
worker.onmessage = function (event) {
document.getElementById('result').textContent += ', ' + event.data;
};
worker.postMessage({from:17,to:50});
</script>
</body>
</html>
и файл worker.js:
onmessage = function(event)
{
var n = event.data.from;
while (n < event.data.to) {
n += 1;
if(isPrime(n)){
// found a prime!
postMessage(n);
}
}
};
function isPrime(number){
for (var i = 2; i <= Math.sqrt(number); i += 1){
if (number % i == 0){
return false;
}
}
return true;
}
Для тех, кто хочет поиграться с примером, – пожалуйста сюда.
Теперь немного комментариев.
var worker = new Worker('worker.js');
создаем объект Worker. Объект не запускается до полной загрузки и выполнения файла. Если путь к объекту Worker возвращает ошибку 404, его выполнение прекращается без уведомлений.
worker.onmessage = function (event) {
прописываем колбэк, который выполнится при вызове метода postMessage внутри воркера
worker.postMessage({from:17,to:50});
отправляем в воркер сообщение с параметрами диапазона ( мы хотим получить все простые числа в диапозоне с 17 до 50 )
Перейдем к коду воркера:
onmessage = function(event){
функция будет вызвана, при обращении к метод worker.postMessage() в основном скрипте
var n = event.data.from;
доступ к данным внутри метода можно получить через event.data
Взаимодействие с объектами Worker
Как уже было показано на примере, взаимодействие с воркерами осуществляется путем потоковой передачи сообщений (метод postMessage – для отправки и колбэк onmessage – для приема).
Колбэк на получение сообщения можно также навешивать следующим способом:
addEventListener('message', function(e) {
именно он является рекомендуемым для использования.
Ограничения Web Worker
К чему можно обращаться внутри worker-скрипта:
- Объект
navigator - Объект
location(только чтение) XMLHttpRequestsetTimeout()/clearTimeout()иsetInterval()/clearInterval()- Кэш приложений
- Импорт внешних скриптов с использованием метода
importScripts() - Создание других объектов Web Worker
У worker-скрипта нет доступа к:
- Модель DOM (она не ориентирована на многопоточное исполнение)
- Объект
window - Объект
document - Объект
parent
Методы postMessage и onmessage являются глобальными для ворвера, и к ним можно обратиться как на прямую, так и через this и self.
Максимальное количество workerов – 256, после чего стек переполняется и пишет ошибку:
Maximum number of Web Worker instances(256) exceeded for this window.
Отлов ошибок
Также, как и с сообщениями, мы можем подписаться на получение ошибок:
worker.addEventListener('error', onError, false);
Динамическое создание worker-скриптов
Иногда возникает необходимо создать worker-скрипт динамически(!внимание именно worker-скрипт, а не сам worker), в зависимости от различных условий. В таком случае создание worker из отдельного файла нас не устраивает и мы должны воспользоваться вторым способом – через подготовленный объект Blob, а точнее – ссылки на него.
Вот так это будет выглядеть в коде
var script = "onmessage = function(e) { postMessage('msg from worker'); }";
var blob = new Blob([script]);
var blobURL = window.URL.createObjectURL(blob);
var worker = new Worker(blobURL);
Для удобства скрипт изначально пожно записать в script тег:
<script id="worker1" type="javascript/worker">
//...код вашего воркера
</script>
а потом получить содержимое:
var script = document.querySelector('#worker1').textContent;
!Внимание: не забудьте поставить тип javascript/worker, это предотвратит разбор кода js-движком браузера.
Литература
- Using web workers
- The Basics of Web Workers ( и русский перевод, который, к сожалению, не обновляется)
- Introduction to HTML5 Web Workers: The JavaScript Multi-threading Approach
- Web Workers
К сожалению, примеров, которые бы показались мне интересными, не нашел. Сам сейчас работаю над более продвинутой версией получения больших простых чисел.