Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Добавляет статью про Web Workers #5190

Merged
merged 19 commits into from Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
189 changes: 189 additions & 0 deletions js/web-workers/index.md
@@ -0,0 +1,189 @@
---
title: "Web Workers"
description: "Улучшите производительность веб-приложений с Web Workers. Используйте фоновые потоки для сложных задач без замедления UI. Узнайте как!"
authors:
- marsel
related:
- ""
tags:
- article
---

<!--
HellSquirrel marked this conversation as resolved.
Show resolved Hide resolved
1. В description есть описание для соцсетей и поисковиков, не больше 200 символов
2. В authors есть ники авторов основного текста
3. В contributors перечислены ники всех соавторов и тех, кто работал над текстом (дописали «На практике»? Переписали блок? Вам сюда)
4. В keywords записаны ключевые слова для SEO: пишем сюда слова или фразы, которых нет в тексте статьи, но по ним могут искать этот материал
5. Удалены все пустые теги в шапке
6. Подпапка автора есть в папке _people/_
7. Демки лежат в подпапке _demos/_
8. В related добавлено три ссылки на материалы Доки, которые будут предлагаться в конце. Не добавляем следующий или предыдущий материал в разделе
-->

## Что такое Web Worker?

Web Worker — это функциональность JavaScript, которая позволяет выполнять скрипты в фоновом режиме параллельно с основным потоком выполнения, не блокируя его и не влияя на производительность пользовательского интерфейса. Это дает возможность выполнять сложные вычисления или обработку данных без замедления отзывчивости веб-страницы.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Может как-то так?)

Suggested change
Web Worker — это функциональность JavaScript, которая позволяет выполнять скрипты в фоновом режиме параллельно с основным потоком выполнения, не блокируя его и не влияя на производительность пользовательского интерфейса. Это дает возможность выполнять сложные вычисления или обработку данных без замедления отзывчивости веб-страницы.
WebWorker — это функциональность JavaScript, которая позволяет выполнять код вне основного потока, не блокируя его. Долгие или сложные вычисления на WebWorkers не блокируют пользовательский интерфейс.


По сути, это скрипт js, который запускается в отдельном потоке браузера и выполняет код параллельно основному потоку браузера. К тому же Web Workers имеют свой собственный контекст, отличный от window.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вот тут надо очень аккуратно. WebWorker (в хроме) выполняется в отдельном isolate https://v8docs.nodesource.com/node-0.8/d5/dda/classv8_1_1_isolate.html. Это отдельный экземпляр JS движка. Это чуть хитрее чем отдельный поток
Давай как-то попробуем это донести? )


Что скрывается под фразой “собственный контекст”? Если упростить, то внутри Web Workers недоступны **`window`**, **`document`** или **`parent`** объекты, то есть нам недоступно управление DOM. По крайней мере, напрямую, но ничто не мешает нам манипулировать DOM косвенно, например, через отправку команд в window-поток с помощью `postMessage`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вот что-то такое :)

Suggested change
Что скрывается под фразой “собственный контекст”? Если упростить, то внутри Web Workers недоступны **`window`**, **`document`** или **`parent`** объекты, то есть нам недоступно управление DOM. По крайней мере, напрямую, но ничто не мешает нам манипулировать DOM косвенно, например, через отправку команд в window-поток с помощью `postMessage`.
Разберёмся что означает “собственный контекст”? Контекст выполнения WebWorkers [WorkerGlobalScope](https://html.spec.whatwg.org/multipage/workers.html#workerglobalscope), отличается от контекста выполнения для обычного потока. В нем недоступны объекты window и document и DOM API. Чтобы управлять DOM можно использовать другой механизм – отправку команд в основной поток через `postMessage`


### Как создать и запустить Web Worker?

Всё достаточно просто: нужно вызывать конструктор Worker и передать туда ссылку на наш js файл с кодом и готово:

```tsx
// window context app.js
const worker = new Worker('worker.js');
```

Давайте теперь разберёмся, как нам связать главный поток браузера и поток Worker. Они общаются между собой с помощью сообщений:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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


```tsx
// window context app.js
const worker = new Worker('worker.js');

worker.postMessage({ message: '415тый, я база, ответьте' });
```

Далее в Web Worker с помощью свойства onmessage, которое находится в глобальном контексте Worker или другими словами `self`, мы записываем туда наш обработчик сообщений из вышестоящего контекста. Обратите внимание, что postMessage или self.postMessage мы можем вызвать из любого места нашего Worker.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я бы написала про self где-то в начале, там где мы говорим про глобальный поток


```tsx
// worker context worker.js
onmessage = function (e) {
if (e.message === "415ый, я база, ответьте") {
postMessage("База, это 415ый, как слышно?");
}
};
```

Давайте теперь это сообщение примем в нашем `app.js`: для этого так же воспользуемся onmessage, но уже у объекта нашего Worker.

```tsx
// window context app.js
const worker = new Worker("worker.js");

worker.postMessage({ message: "415тый, я база, ответьте" });

worker.onmessage = function (e) {
console.log(e); // База, это 415тый, как слышно?
};
```

Внимательный читатель заметит, что в Worker я отправлял объект со свойством `message`, а уже из Worker ответ был простой строкой. Дело в том, что мы можем передавать в postMessage какой угодно объект или тип данных.

## Tеперь давайте глубже погрузимся в Web Workers

Ранее я упоминал что нам недоступен объект window, но что вообще доступно внутри Web Worker? На самом деле, много всего, но я перечислю лишь некоторые функции Browser API, которые мы чаще всего используем — это fetch, setInterval, setTimeout, requestAnimationFrame, queueMicrotask. Полный список поддерживаемых API можно найти [тут](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers)

Выше мы с вами рассмотрели Dedicated Worker, это Worker, который доступен только в скрипте, который его создал. В случаях когда в Worker есть своё состояние, оно будет доступно только в скрипте, который создал этот Worker. Но что, если мы хотим, чтобы инстанс нашего Worker был доступен из разных вкладок приложения и из разных мест приложения? Такими возможностями обладают Shared Workers.

### Shared Workers

Shared Workers в веб-разработке — это тип Web Workers, который позволяет создать многопоточный, параллельный поток выполнения, разделяемый между несколькими вкладками, iframe или окнами в пределах одного и того же происхождения (origin). Это означает, что Shared Worker может быть использован одновременно несколькими частями веб-приложения для обмена данными, синхронизации состояний или выполнения фоновых задач без необходимости повторной загрузки или дублирования в каждой вкладке или окне. Тут стоит отметить, что состояние Shared Worker будет живо, пока о нём кто-то помнит.

Давайте теперь разберёмся, как нам создать и запустить Shared Worker.

В целом, всё так же, как и с Dedicated Worker, но с некоторыми исключениями. Во-первых, для создания инстанса SharedWorker нужно использовать объект/класс SharedWorker. Во-вторых, работа с `onmessage` и `postMessage` происходит у свойства `port`:

```tsx
// app1.js
const sharedWorker = new SharedWorker("worker.js");

sharedWorker.port.onmessage = (event) => {
console.log("data from worker", event);
};

const sendDataToWorker = () => {
sharedWorker.port.postMessage(1);
};
```

Далее то же самое делаем в другом скрипте:

```tsx
// app2.js
const sharedWorker = new SharedWorker("worker.js");

sharedWorker.port.onmessage = (event) => {
console.log("data from worker", event);
};

const sendDataToWorker = () => {
sharedWorker.port.postMessage(2);
};
```

Давайте теперь посмотрим устройство нашего SharedWorker.

```tsx
// worker.js
let sum = 0;

onconnect = (connect) => {
// всегда будет в ports один port
port = connect.ports[0];

port.onmessage = (event) => {
sum += event;
};

port.postMessage(sum);
};
```

Внутри `onconnect` мы принимаем event, - в нашем случае мы называем его `connect`, - и достаём оттуда объект `port`, - он всегда в этом массиве один, почему его сделали массивом не очень понятно, возможно, это задел на будущее. Далее мы берём port и уже на нём выполняем подписку на входящие message. С отправкой message алгоритм такой же, она осуществляется через объект `port`.

### Вложенность Web Workers

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

### Импорты в Web Workers

Начиная с версий браузеров, выпущенных в июне 2023 года, практически все браузеры поддерживают ES module в контексте Workers, поэтому можно использовать импорты с помощью конструкции `import xxxxx from ‘lib’`. Эта информация пригодится вам при настройке сборки приложения.

### Отправка данных в Web Worker

Отправка данных в Web Worker и из него обычно осуществляется через механизм сообщений, использующий метод `postMessage()`. Когда вы отправляете данные в Web Worker (или обратно в основной поток из Worker), данные по умолчанию копируются, что может привести к дополнительным затратам в производительности, особенно при работе с большими объемами данных.

### Отправка данных без копирования

Для оптимизации производительности и минимизации затрат на копирование данных можно использовать технику передачи данных через *Transferable objects*. Transferable objects позволяют обмениваться данными между основным потоком и Web Worker (или между Web Workers) без копирования, передавая владение данными из одного контекста в другой. Это означает, что источник теряет доступ к переданным данным после отправки. Примерами transferable objects являются `ArrayBuffer` и `MessagePort`.

### Пример использования Transferable objects

Отправка данных в Web Worker:

```jsx
// Создание ArrayBuffer
var buffer = new ArrayBuffer(1024); // 1024 байта

// Отправка ArrayBuffer в Worker
worker.postMessage(buffer, [buffer]);

```

Прием данных в Web Worker:

```jsx
onmessage = function(e) {
var buffer = e.data; // Получение ArrayBuffer
// Можно начать работу с данными
};

```

В этом примере `ArrayBuffer` отправляется в Web Worker с использованием `postMessage`, и сам `ArrayBuffer` указывается во втором аргументе `postMessage` как transferable object. Это означает, что после отправки `ArrayBuffer` из основного потока он больше не доступен в этом потоке, а владение им передается Web Worker.

### Ограничения

- После передачи transferable object, источник теряет доступ к объекту. Это значит, что объект нельзя использовать в источнике после его отправки.
- Не все объекты могут быть переданы как transferable. Типы объектов, которые можно передавать таким образом, ограничены и включают в себя `ArrayBuffer` и `MessagePort`.

### Преимущества Transferable objects

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

## Заключение:

Web Workers представляют собой мощный инструмент для разработки более отзывчивых и производительных веб-приложений. Они позволяют разгрузить основной поток от тяжелых вычислений и фоновых задач, обеспечивая тем самым лучший пользовательский опыт. Однако необходимо учитывать их ограничения и особенности реализации для эффективного использования в своих проектах.
13 changes: 13 additions & 0 deletions people/marsel/index.md
@@ -0,0 +1,13 @@
---
name: 'Марсель Ахметшин'
url: https://github.com/ByMarsel/
photo: photo.jpg
badges:
- first-contribution-small
---

Привет 😎

Я фронтенд-разработчик в компании Tabby, люблю клепать красивые анимации и ковырять Browser API.

Так же я иногда пишу статейки на [dev.to](https://dev.to/bymarsel/web-workers-revolutionizing-web-performance-and-user-experience-3i1b), поэтому подписывайся! 💪
Binary file added people/marsel/photo.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.