Workers в жизни и в экосистеме Angular презентация

Содержание

Слайд 2

Обо мне

Духовняк Александр
Старший разработчик, Tinkoff.ru
shanhaichik@gmail.com https://github.com/shanhaichik

Обо мне Духовняк Александр Старший разработчик, Tinkoff.ru shanhaichik@gmail.com https://github.com/shanhaichik

Слайд 3

Будем говорить о том:

Будем говорить о том:

Слайд 4

Будем говорить о том:

Будем говорить о том:

Слайд 5

Демо №1

Рассмотрим на примере!

https://github.com/shanhaichik/angular-meetup-worker-demo

Демо №1 Рассмотрим на примере! https://github.com/shanhaichik/angular-meetup-worker-demo

Слайд 6

Рассмотрим

Что такое Web Worker и какие задачи решает

Рассмотрим Что такое Web Worker и какие задачи решает

Слайд 7

Рассмотрим

Что такое Web Worker и какие задачи решает

Как создается и конфигурируется Web Worker

Рассмотрим Что такое Web Worker и какие задачи решает Как создается и конфигурируется Web Worker

Слайд 8

Рассмотрим

Что такое Web Worker и какие задачи решает

Как создается и конфигурируется Web Worker

Как

происходит взаимодействие с Web Worker и какие типы данных можно в него передавать

Рассмотрим Что такое Web Worker и какие задачи решает Как создается и конфигурируется

Слайд 9

Рассмотрим

Что такое Web Worker и какие задачи решает

Как создается и конфигурируется Web Worker

Как

происходит взаимодействие с Web Worker и какие типы данных можно в него передавать

DedicatedWorkerGlobalScop

Рассмотрим Что такое Web Worker и какие задачи решает Как создается и конфигурируется

Слайд 10

Web Worker - это

Инструмент для асинхронной загрузки сторонних скриптов и запуска их выполнения

в отдельном потоке

Web Worker - это Инструмент для асинхронной загрузки сторонних скриптов и запуска их

Слайд 11

Web Worker - это

Инструмент для асинхронной загрузки сторонних скриптов и запуска их выполнения

в отдельном потоке

Инструмент решающий проблему однопоточности в JS

Web Worker - это Инструмент для асинхронной загрузки сторонних скриптов и запуска их

Слайд 12

Создание обычным программистом

https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker
const options = { name: 'MyWorker’ };
const worker = new Worker(’getMyWorker.js’,

options);

Создание обычным программистом https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker const options = { name: 'MyWorker’ }; const worker

Слайд 13

Создание скучающим программистом

https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker
const code = new Blob([`(${worker_code.toString()})()`], {type: 'text/javascript'});
const url = URL.createObjectURL(code);
const worker

= new Worker(url, options);

Создание скучающим программистом https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker const code = new Blob([`(${worker_code.toString()})()`], {type: 'text/javascript'}); const url

Слайд 14

Получение и отправка данных

https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker
worker.addEventListener('message', event => { console.log(event.data); }, false);

Получение и отправка данных https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker worker.addEventListener('message', event => { console.log(event.data); }, false);

Слайд 15

Получение и отправка данных

https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker
const params = { count: 10, size: 300 }
worker.postMessage(JSON.stringify(params));

Получение и отправка данных https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker const params = { count: 10, size: 300 } worker.postMessage(JSON.stringify(params));

Слайд 16

Возможные типы данных

Строки

Возможные типы данных Строки

Слайд 17

Возможные типы данных

Строки

Любые сериализуемые объекты

Возможные типы данных Строки Любые сериализуемые объекты

Слайд 18

Возможные типы данных

Строки

Любые сериализуемые объекты

ArrayBuffer

Возможные типы данных Строки Любые сериализуемые объекты ArrayBuffer

Слайд 19

Важно помнить

Данные передаваемы между потоками КОПИРУЮТСЯ!

Важно помнить Данные передаваемы между потоками КОПИРУЮТСЯ!

Слайд 20

Важно помнить

Данные передаваемы между потоками КОПИРУЮТСЯ!

50Mb = 300-500ms

Важно помнить Данные передаваемы между потоками КОПИРУЮТСЯ! 50Mb = 300-500ms

Слайд 21

Получение и отправка данных

Передача управления из потока в поток (TransferList)
const data = new

ArrayBuffer(1000); worker.postMessage(data, [data]);
console.log(data.byteLength); // 0

Получение и отправка данных Передача управления из потока в поток (TransferList) const data

Слайд 22

Важно помнить

50Mb = 10-20ms

Важно помнить 50Mb = 10-20ms

Слайд 23

Как создается и конфигурируется Web Worker?
const someTask = params => {/* just do

it */}; self.addEventListener('message', event => { const result = someTask(event); self.postMessage(result); }, false);

https://developer.mozilla.org/ru/docs/Web/API/DedicatedWorkerGlobalScope

Как создается и конфигурируется Web Worker? const someTask = params => {/* just

Слайд 24

Как создается и конфигурируется Web Worker?

const someTask = params => {/* just do

it */}; self.addEventListener('message', event => { const result = someTask(event); self.postMessage(result); }, false);

https://developer.mozilla.org/ru/docs/Web/API/DedicatedWorkerGlobalScope

Как создается и конфигурируется Web Worker? const someTask = params => {/* just

Слайд 25

Как создается и конфигурируется Web Worker?

В Dedicated Worker Global Scope есть доступ к:

https://developer.mozilla.org/ru/docs/Web/API/DedicatedWorkerGlobalScope

к объекту location

Как создается и конфигурируется Web Worker? В Dedicated Worker Global Scope есть доступ

Слайд 26

Как создается и конфигурируется Web Worker?

В Dedicated Worker Global Scope есть доступ к:

https://developer.mozilla.org/ru/docs/Web/API/DedicatedWorkerGlobalScope

к объекту location

к объекту navigator

Как создается и конфигурируется Web Worker? В Dedicated Worker Global Scope есть доступ

Слайд 27

Как создается и конфигурируется Web Worker?

В Dedicated Worker Global Scope есть доступ к:

https://developer.mozilla.org/ru/docs/Web/API/DedicatedWorkerGlobalScope

к объекту location

к объекту navigator

к объектам XMLHttpRequest/WebSocket

Как создается и конфигурируется Web Worker? В Dedicated Worker Global Scope есть доступ

Слайд 28

Как создается и конфигурируется Web Worker?

В Dedicated Worker Global Scope есть доступ к:

https://developer.mozilla.org/ru/docs/Web/API/DedicatedWorkerGlobalScope

к объекту location

к объекту navigator

к объектам XMLHttpRequest/WebSocket

setTimeout / setInterval

Как создается и конфигурируется Web Worker? В Dedicated Worker Global Scope есть доступ

Слайд 29

Как создается и конфигурируется Web Worker?

В Dedicated Worker Global Scope есть доступ к:

https://developer.mozilla.org/ru/docs/Web/API/DedicatedWorkerGlobalScope

к объекту location

к объекту navigator

к объектам XMLHttpRequest/WebSocket

setTimeout / setInterval

name – имя воркера

Как создается и конфигурируется Web Worker? В Dedicated Worker Global Scope есть доступ

Слайд 30

Как создается и конфигурируется Web Worker?

В Dedicated Worker Global Scope есть доступ к:

https://developer.mozilla.org/ru/docs/Web/API/DedicatedWorkerGlobalScope

к объекту location

к объекту navigator

к объектам XMLHttpRequest/WebSocket

setTimeout / setInterval

name – имя воркера

importScripts([]) – метод для асинхронной подгрузки скриптов

Как создается и конфигурируется Web Worker? В Dedicated Worker Global Scope есть доступ

Слайд 31

Как создается и конфигурируется Web Worker?

В Dedicated Worker Global Scope есть доступ к:

https://developer.mozilla.org/ru/docs/Web/API/DedicatedWorkerGlobalScope

к объекту location

к объекту navigator

к объектам XMLHttpRequest/WebSocket

setTimeout / setInterval

name – имя воркера

importScripts([]) – метод для асинхронной подгрузки скриптов

к другим видам сервисов и объектов (см. ссылку)

Как создается и конфигурируется Web Worker? В Dedicated Worker Global Scope есть доступ

Слайд 32

Как создается и конфигурируется Web Worker?

В Dedicated Worker Global Scope нет доступа:

к

DOM

Как создается и конфигурируется Web Worker? В Dedicated Worker Global Scope нет доступа: к DOM

Слайд 33

Как создается и конфигурируется Web Worker?

В Dedicated Worker Global Scope нет доступа:

к

DOM

к объекту document

Как создается и конфигурируется Web Worker? В Dedicated Worker Global Scope нет доступа:

Слайд 34

Как создается и конфигурируется Web Worker?

В Dedicated Worker Global Scope нет доступа:

к

DOM

к объекту document

к объекту window

Как создается и конфигурируется Web Worker? В Dedicated Worker Global Scope нет доступа:

Слайд 35

Как создается и конфигурируется Web Worker?

В Dedicated Worker Global Scope нет доступа:

к

DOM

к объекту document

к объекту window

к объекту parent

Как создается и конфигурируется Web Worker? В Dedicated Worker Global Scope нет доступа:

Слайд 36

Как это выглядит

Как это выглядит

Слайд 37

Выводы

Web Worker - это внешний подгружаемый и исполняемый файл

Выводы Web Worker - это внешний подгружаемый и исполняемый файл

Слайд 38

Выводы

Web Worker - это внешний подгружаемый и исполняемый файл

Это изолированный процесс со своей

областью видимости

Выводы Web Worker - это внешний подгружаемый и исполняемый файл Это изолированный процесс

Слайд 39

Выводы

Web Worker - это внешний подгружаемый и исполняемый файл

Это изолированный процесс со своей

областью видимости

Выполнение кода и задач происходит параллельно

Выводы Web Worker - это внешний подгружаемый и исполняемый файл Это изолированный процесс

Слайд 40

Выводы

Web Worker - это внешний подгружаемый и исполняемый файл

Это изолированный процесс со своей

областью видимости

Выполнение кода и задач происходит параллельно

Коммуникация построена на основе сообщений

Это наше настоящее!

Выводы Web Worker - это внешний подгружаемый и исполняемый файл Это изолированный процесс

Слайд 41

Sub Workers

39

Sub Workers 39

Слайд 42

Sub Workers

39

Sub Workers 39

Слайд 43

Демо №2

Рассмотрим пример с Web Worker-ом!

https://github.com/shanhaichik/angular-meetup-worker-demo

Демо №2 Рассмотрим пример с Web Worker-ом! https://github.com/shanhaichik/angular-meetup-worker-demo

Слайд 44

Shared Workers

Shared Workers

Слайд 45

Преимущества Shared Workers

Синхронизация между вкладками без костылей

Преимущества Shared Workers Синхронизация между вкладками без костылей

Слайд 46

Преимущества Shared Workers

Синхронизация между вкладками без костылей

Оптимизация сетевых ресурсов и ресурсов системы

Преимущества Shared Workers Синхронизация между вкладками без костылей Оптимизация сетевых ресурсов и ресурсов системы

Слайд 47

Преимущества Shared Workers

Синхронизация между вкладками без костылей

Оптимизация сетевых ресурсов и ресурсов системы

Борьба с

человеком вкладкой

и многие другие…

Преимущества Shared Workers Синхронизация между вкладками без костылей Оптимизация сетевых ресурсов и ресурсов

Слайд 48

Получение и отправка данных

https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker

const worker = new SharedWorker(’getMyWorker.js’); worker.port.onmessage = event => { console.log(event.data); }
worker.port.start();

Получение и отправка данных https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker const worker = new SharedWorker(’getMyWorker.js’); worker.port.onmessage = event

Слайд 49

Получение и отправка данных

https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker

const worker = new SharedWorker(’getMyWorker.js’); worker.port.onmessage = event => { console.log(event.data); }
worker.port.start();

Получение и отправка данных https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker const worker = new SharedWorker(’getMyWorker.js’); worker.port.onmessage = event

Слайд 50

Получение и отправка данных

https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker

const worker = new SharedWorker(’getMyWorker.js’); worker.port.onmessage = event => { console.log(event.data); }
worker.port.start();

Получение и отправка данных https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker const worker = new SharedWorker(’getMyWorker.js’); worker.port.onmessage = event

Слайд 51

Получение и отправка данных

https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker

onconnect = (e) => { const port = e.ports[0]; port.addEventListener('message',

function(e) { port.postMessage('hi'); }); port.start(); }

Получение и отправка данных https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker onconnect = (e) => { const port =

Слайд 52

Web Workers в Angular

Web Workers в Angular

Слайд 53

Демо №3

Рассмотрим пример Angular приложения

https://github.com/shanhaichik/angular-workers-in-action

Демо №3 Рассмотрим пример Angular приложения https://github.com/shanhaichik/angular-workers-in-action

Слайд 54

Web Worker в Angular – это механизм для выноса и запуска Angular приложения

в отдельном потоке.

Web Worker в Angular - это

Web Worker в Angular – это механизм для выноса и запуска Angular приложения

Слайд 55

Решает следующие задачи

Дает возможность распараллелить работу приложения

Решает следующие задачи Дает возможность распараллелить работу приложения

Слайд 56

Решает следующие задачи

Дает возможность распараллелить работу приложения

Вынести сложные части бизнес логики в

отдельный поток

Решает следующие задачи Дает возможность распараллелить работу приложения Вынести сложные части бизнес логики в отдельный поток

Слайд 57

Решает следующие задачи

Дает возможность распараллелить работу приложения

Вынести сложные части бизнес логики в

отдельный поток

Снять нагрузку с основного потока, тем самым обеспечить наиболее эффективное манипулирование с DOM и работу с анимацией, что улучшает работу UI и увеличивает FPS.

Решает следующие задачи Дает возможность распараллелить работу приложения Вынести сложные части бизнес логики

Слайд 58

Решает следующие задачи

Дает возможность распараллелить работу приложения

Вынести сложные части бизнес логики в

отдельный поток

Снять нагрузку с основного потока, тем самым обеспечить наиболее эффективное манипулирование с DOM и работу с анимацией, что улучшает работу UI и увеличивает FPS.

Закладывать более гибкую архитектуру приложения

Решает следующие задачи Дает возможность распараллелить работу приложения Вынести сложные части бизнес логики

Слайд 59

Решает следующие задачи

Дает возможность распараллелить работу приложения

Вынести сложные части бизнес логики в

отдельный поток

Снять нагрузку с основного потока, тем самым обеспечить наиболее эффективное манипулирование с DOM и работу с анимацией, что улучшает работу UI и увеличивает FPS.

Закладывать более гибкую архитектуру приложения

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

Решает следующие задачи Дает возможность распараллелить работу приложения Вынести сложные части бизнес логики

Слайд 60

Шаг 1: Установить нужные зависимости.
npm install -S @angular/platform-webworker
@angular/platform-webworker-dynamic

Конфигурация Web Worker в

Angular

Шаг 1: Установить нужные зависимости. npm install -S @angular/platform-webworker @angular/platform-webworker-dynamic Конфигурация Web Worker в Angular

Слайд 61

Шаг 2: Обновить импорты основного модуля

Конфигурация Web Worker в Angular

в файле app.module.ts


заменяем BrowserModule
на WorkerAppModule
из пакета @angular/platform-webworker

Шаг 2: Обновить импорты основного модуля Конфигурация Web Worker в Angular в файле

Слайд 62

Шаг 3: Добавить загрузчик worker-a

Конфигурация Web Worker в Angular

в файле main.ts
заменяем

platformBrowserDynamic().bootstrapModule(AppModule);
на bootstrapWorkerUi(’worker.bootstrap.bundle.js').then(afterBootstrap);
из пакета @angular/platform-webworker

Шаг 3: Добавить загрузчик worker-a Конфигурация Web Worker в Angular в файле main.ts

Слайд 63

Шаг 4: Создать файл worker-a для запуска приложения

Конфигурация Web Worker в Angular

import 'polyfills.ts'; import

'@angular/core'; import '@angular/common'; import { platformWorkerAppDynamic } from '@angular/platform-webworker-dynamic'; import { AppModule } from './app/app.module'; platformWorkerAppDynamic().bootstrapModule(AppModule);

Шаг 4: Создать файл worker-a для запуска приложения Конфигурация Web Worker в Angular

Слайд 64

Вот и все, вы в Треде!

Вот и все, вы в Треде!

Слайд 65

Ограничения

Такие же ограничения как и обычного Web Worker-a
(DOM, window и т.д.)

Ограничения Такие же ограничения как и обычного Web Worker-a (DOM, window и т.д.)

Слайд 66

Ограничения

Такие же ограничения как и обычного Web Worker-a
(DOM, window и т.д.)

Но есть

нюанс ☺

Ограничения Такие же ограничения как и обычного Web Worker-a (DOM, window и т.д.)

Слайд 67

Ограничения

Такие же ограничения как и обычного Web Worker-a
(DOM, window и т.д.)

Но есть

нюанс ☺

В приложениях работающих в Web Worker-е есть возможность производить манипуляции с DOM

Используя возможность Renderer/Renderer2

Ограничения Такие же ограничения как и обычного Web Worker-a (DOM, window и т.д.)

Слайд 68

Ограничения

Такие же ограничения как и обычного Web Worker-a
(DOM, window и т.д.)

Но есть

нюанс ☺

В приложениях работающих в Web Worker-е есть возможность производить манипуляции с DOM

Используя возможность Renderer/Renderer2

С помощью Data bindings

Ограничения Такие же ограничения как и обычного Web Worker-a (DOM, window и т.д.)

Слайд 69

RenderCompiler

Core + App

UI Tread

Worker Tread

Routing channel

Events channel

Render channel

RenderCompiler Core + App UI Tread Worker Tread Routing channel Events channel Render channel

Слайд 70

Получение и отправка данных

MessageBus - это абстракция низкого уровня, которая предоставляет API-интерфейс для

связи с компонентами приложения в реальном времени. (MessageBus стоит использовать если мы хотим контролировать протокол обмена сообщениями)

Получение и отправка данных MessageBus - это абстракция низкого уровня, которая предоставляет API-интерфейс

Слайд 71

Получение и отправка данных

MessageBroker - это абстракция сообщений верхнего уровня, которая находится поверх

MessageBus. Она используется, когда мы хотим выполнить код в воркере и хотим получить результат.

MessageBus - это абстракция низкого уровня, которая предоставляет API-интерфейс для связи с компонентами приложения в реальном времени. (MessageBus стоит использовать если мы хотим контролировать протокол обмена сообщениями)

Получение и отправка данных MessageBroker - это абстракция сообщений верхнего уровня, которая находится

Слайд 72

MessageBroker

MessageBroker

Слайд 73

MessageBroker в основном треде

bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { const bRef = ref.injector.get(ClientMessageBrokerFactory);
// код…

}

MessageBroker в основном треде bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { const bRef = ref.injector.get(ClientMessageBrokerFactory); // код… }

Слайд 74

MessageBroker в основном треде

bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { // код…
bRef.createMessageBroker(CHANNEL, RUN_IN_ZONE);
// код…

}

MessageBroker в основном треде bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { // код… bRef.createMessageBroker(CHANNEL, RUN_IN_ZONE); // код… }

Слайд 75

MessageBroker в основном треде

bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { // код…
const fnArg = new

FnArg(data, SerializerTypes.PRIMITIVE);
const sendData = new UiArguments(channel, [fnArg]); // код… }

MessageBroker в основном треде bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { // код… const fnArg

Слайд 76

MessageBroker в основном треде

bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { // код…
appBrokerRef.runOnService(sendData, SerializerTypes.PRIMITIVE)
.then(result => {

console.log('From worker: ’, result); });;
// код… }

MessageBroker в основном треде bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { // код… appBrokerRef.runOnService(sendData, SerializerTypes.PRIMITIVE)

Слайд 77

MessageBroker в воркере

export class SomeComponent implements OnInit { constructor(private broker: ServiceMessageBrokerFactory { }

ngOnInit() { const broker = this.broker.createMessageBroker(CHANNEL); broker.registerMethod(CHANNEL, [SerializerTypes.PRIMITIVE], () => { // Код... return Promise.resolve('Ответ с воркера'); }, SerializerTypes.PRIMITIVE); } }

MessageBroker в воркере export class SomeComponent implements OnInit { constructor(private broker: ServiceMessageBrokerFactory {

Слайд 78

MessageBus

MessageBus

Слайд 79

MessageBus в основном треде

bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { const bRef = ref.injector.get(MessageBus);
// код…

}

MessageBus в основном треде bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { const bRef = ref.injector.get(MessageBus); // код… }

Слайд 80

MessageBus в основном треде

bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { // код…
bRef.initChannel(CHANNEL);
// код…

}

MessageBus в основном треде bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { // код… bRef.initChannel(CHANNEL); // код… }

Слайд 81

MessageBus в основном треде

bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { // код…
bRef.to(CHANNEL).emit(sendData);
// код…

}

MessageBus в основном треде bootstrapWorkerUi(’worker.js').then(afterBootstrap); function afterBootstrap(ref: PlatformRef) { // код… bRef.to(CHANNEL).emit(sendData); // код… }

Слайд 82

MessageBus в воркере

export class SomeComponent implements OnInit { constructor(private broker: MessageBus){ } ngOnInit()

{
this.broker.initChannel(CHANNEL);
this.broker.from(CHANNEL).subscribe(message => {
console.log(message);
}); } }

MessageBus в воркере export class SomeComponent implements OnInit { constructor(private broker: MessageBus){ }

Слайд 83

Роутинг

Роутинг

Слайд 84

Роутинг

import {
bootstrapWorkerUi,
WORKER_UI_LOCATION_PROVIDERS
} from '@angular/platform-webworker';

bootstrapWorkerUi('webworker.js’, [WORKER_UI_LOCATION_PROVIDERS]);

Роутинг import { bootstrapWorkerUi, WORKER_UI_LOCATION_PROVIDERS } from '@angular/platform-webworker'; bootstrapWorkerUi('webworker.js’, [WORKER_UI_LOCATION_PROVIDERS]);

Слайд 85

Роутинг
providers: [ WORKER_APP_LOCATION_PROVIDERS, {provide: APP_BASE_HREF, useValue : '/' } ],

Роутинг providers: [ WORKER_APP_LOCATION_PROVIDERS, {provide: APP_BASE_HREF, useValue : '/' } ],

Слайд 86

Общие выводы

Использовать Web Worker-ы не только классно, но и полезно. Это наше настоящее

и не стоит их бояться.

Общие выводы Использовать Web Worker-ы не только классно, но и полезно. Это наше

Имя файла: Workers-в-жизни-и-в-экосистеме-Angular.pptx
Количество просмотров: 78
Количество скачиваний: 0