JavaScript

JavaScript Web Workers: руководство для начинающих

Spread the love

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

Когда JavaScript был задуман в первые дни Интернета, направление развития Интернета было неясно. Из-за постоянных, быстрых изменений в отрасли и экосистеме, необходимости обратной совместимости браузеров и веб-стандартов развитие JavaScript стало постоянным потоком исправлений, взломов и последствий.

Современные мобильные устройства обычно поставляются с 8+ ядрами ЦП или 12+ ядрами GPU. Настольные и серверные процессоры имеют до 16 ядер, 32 потока или более.

В этой среде узкое место имеет наличие доминирующей однопоточной среды программирования или сценариев.

JavaScript является однопоточным

Это означает, что по замыслу движки JavaScript — изначально браузеры — имеют один основной поток выполнения, и, проще говоря, процесс или функция B() не могут быть выполнены до тех пор, пока процесс или функция A() не будут завершены. Пользовательский интерфейс веб-страницы не реагирует на любую другую обработку JavaScript, в то время как он занят выполнением чего-либо — это называется блокировкой DOM.

Это ужасно неэффективно, особенно по сравнению с другими языками.

Если мы перейдем к JS Bin и запустим этот код в консоли JavaScript браузера:

//noprotect
i = 0;
while (i < 60000) {
  console.log("The number is " + i);
  i++;
}

… сайт jsbin.com перестанет отвечать на запросы до тех пор, пока браузер не посчитает и не отобразит до 60 000 строк.

Мы не сможем взаимодействовать с чем-либо на странице, потому что браузер будет занят в этот момент.

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

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

Web Workers

W3C опубликовал первый проект стандарта web worker в 2009 году. С полной спецификацией можно ознакомиться на веб-сайте Рабочей группы по технологиям веб-гипертекстовых приложений (Web Hypertext Application Technology Working Group website)- или WHATWG — альтернативной W3C организации по веб-стандартам.

Web worker — это асинхронная система или протокол, позволяющий веб-страницам выполнять задачи в фоновом режиме независимо от основного потока и пользовательского интерфейса веб-сайта. Это изолированная среда, которая изолирована от объекта window, объекта document, прямого доступа в Интернет и лучше всего подходит для длительных или сложных вычислительных задач.

Помимо web worker — системы, предназначенной для многопоточности, — существуют и другие способы выполнения асинхронной обработки в JavaScript, такие как асинхронные вызовы Ajax и цикл обработки событий (event loop).

Чтобы продемонстрировать работу web worker, вернемся к JS Bin и попробуем этот фрагмент кода:

console.log("A");
setTimeout(function(){console.log("B");},2000);
console.log("C");
setTimeout(function(){console.log("D");},0);
console.log("E");
setTimeout(function(){console.log("F");},1000);

Когда мы запустим этот пример, последовательность вывода будет — A, C, E, D, F, B. Браузер сначала выполнит операции без тайм-аута по мере их поступления, а затем выполнит функции setTimeout() в порядке указанной задержки. Однако эта асинхронность не должна автоматически сопоставляться с многопоточностью. В зависимости от хост-машины это часто может быть просто однопотоковый стек вызовов в порядке, который мы объяснили.

Web Workers и многопоточность

Как объясняется на справочном веб-сайте JavaScript в Mozilla, web worker являются «средством для веб-контента запускать сценарии в фоновых потоках».

Мы используем их следующим образом: мы проверяем наличие конструктора Worker() в браузере, и, если он доступен, мы создаем экземпляр рабочего объекта с URL-адресом сценария в качестве аргумента. Этот скрипт будет выполняться в отдельном потоке.

Сценарий должен обслуживаться с того же хоста или домена из соображений безопасности, и это также будет причиной того, что web worker не будут работать, если мы откроем файл локально с помощью схемы file://.

if (typeof(Worker) !== "undefined") {  
    worker = new Worker("worker.js");
}            

Теперь мы напишем этот код в файле worker.js:

i = 0;
while (i < 200000) {
    postMessage("Web Worker Counter: " + i);
    i++;
}

Если вы хотите писать высококачественный код JavaScript с web worker, ознакомьтесь с нашей книгой «JavaScript: Best Practice».

Разделение потоков

Здесь важно отметить разделение область видимости window и document в потоке основного окна браузера и worker.

Чтобы использовать поток worker, эти две области должны иметь возможность обмениваться данными. Для этого мы используем функцию postMessage() в файле worker.js — для отправки сообщений в основной поток браузера — и listener (слушиватель) worker.onmessage в главном потоке для получения сообщений от worker.

Мы также можем отправлять сообщения из основного потока браузера в поток worker. Единственное отличие состоит в том, что мы вызываем worker.postMessage() в главном потоке, а onmessage в потоке worker.

Мы можем использовать метод terminate(), чтобы завершить выполнение worker.

Имея все это в виду, мы подходим к этому примеру:

index.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Web Workers Example</title>

    <style type="text/css">
    body {padding-top:28px;}
    .output-cont {margin-left:12%; margin-top:28px;}

    .output-cont h3 {width:200px; height:100%;}
    .output-cont button {padding:4px 8px; font-size:1.1rem; font-family:sans-serif;  }


    </style>
</head>

<body>

<div class="output-cont"><button >



и worker.js:

i = 0;
while (i < 200000) {
    postMessage("Web Worker Counter: " + i);
    i++;
}

Этот пример дает нам возможность проверить влияние выполнения основного потока на поведение и производительность страницы по сравнению с эффектами web worker.

В этом уроке мы использовали http-server для локального обслуживания файлов.

Теперь мы можем видеть, что рабочий поток не блокирует интерактивность основного процесса браузера, а циклический вывод 200 000 номеров не влияет на основной поток. Числа в элементе #workerOutput обновляются на каждой итерации.

Блокирующий поток, или основной поток, когда задействован в цикле, блокирует всю интерактивность (здесь мы установили число итераций 200 000, но это будет еще более очевидно, если мы увеличим его до 2 000 000).

Еще одна вещь, которая указывает нам на заблокированный основной поток, заключается в том, что рабочий процесс обновляет страницу на каждой итерации, а цикл в главном потоке (определенный в index.html) обновляет только элемент #mainThreadOutput на последней итерации.

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

Заключение

В этой статье мы представили web workers, технологию, которая помогает веб-индустрии идти в ногу со все более и более требовательными веб-приложениями. Это достигается путем предоставления веб-приложениям возможности использовать многопроцессорные и многопоточные устройства, добавляя некоторые многопоточные суперспособности в JavaScript.

Есть ли у вас какие-либо советы, касающиеся web worker и Интернета как платформы для программирования? Дайте нам знать об этом в комментариях! (в блоге автора статьи)

Оригинал: JavaScript Web Workers: A Beginner’s Guide

Была ли вам полезна эта статья?
[11 / 4.5]

Spread the love
Editorial Team

Recent Posts

Vue 3.4 Новая механика v-model компонента

Краткий перевод: https://vuejs.org/guide/components/v-model.html Основное использование​ v-model используется для реализации двусторонней привязки в компоненте. Начиная с Vue…

12 месяцев ago

Анонс Vue 3.4

Сегодня мы рады объявить о выпуске Vue 3.4 «🏀 Slam Dunk»! Этот выпуск включает в…

12 месяцев ago

Как принудительно пере-отобразить (re-render) компонент Vue

Vue.js — это универсальный и адаптируемый фреймворк. Благодаря своей отличительной архитектуре и системе реактивности Vue…

2 года ago

Проблемы с установкой сертификата на nginix

Недавно, у меня истек сертификат и пришлось заказывать новый и затем устанавливать на хостинг с…

2 года ago

Введение в JavaScript Temporal API

Каким бы ни было ваше мнение о JavaScript, но всем известно, что работа с датами…

2 года ago

Когда и как выбирать между медиа запросами и контейнерными запросами

Все, кто следит за последними событиями в мире адаптивного дизайна, согласятся, что введение контейнерных запросов…

2 года ago