Podział sieci na wątki za pomocą instancji roboczych modułów

Moduły JavaScript w WebWorks ułatwiają teraz przenoszenie ciężkich zadań do wątków w tle.

JavaScript jest jednowątkowy, co oznacza, że może wykonywać tylko jedną operację naraz. To jest intuicyjny i dobrze sprawdza się w wielu przypadkach w internecie, ale może stanowić problem, wykonywanie najcięższych zadań, takich jak przetwarzanie danych, analizowanie, obliczanie lub analiza. i coraz częściej złożonych aplikacji jest dostarczanych przez internet, o przetwarzaniu danych.

Na platformie internetowej głównym elementem podstawowym dla wątki i równoległości jest pole Sieć Workers API. Instancje robocze stanowią prostą abstrakcję na systemie operacyjnym wątki udostępniające interfejs API, który pozytywnie wpłynie na wiadomość do komunikacji między wątkami. Może to być bardzo przydatne przy wykonywaniu kosztownych obliczeń lub działające na dużych zbiorach danych, dzięki czemu wątek główny działa płynnie podczas na kosztowne operacje na co najmniej 1 wątku w tle.

Oto typowy przykład użycia instancji roboczych, w którym skrypt instancji roboczej nasłuchuje wiadomości z serwera głównego i odpowiada, wysyłając własną wiadomość:

page.js:

const worker = new Worker('worker.js');
worker.addEventListener('message', e => {
  console.log(e.data);
});
worker.postMessage('hello');

worker.js:

addEventListener('message', e => {
  if (e.data === 'hello') {
    postMessage('world');
  }
});

Interfejs Web Worker API jest dostępny w większości przeglądarek od ponad 10 lat. Mimo że co oznacza, że pracownicy dobrze radzą sobie z przeglądarkami i są dobrze zoptymalizowany, są starsze niż moduły JavaScript. Ponieważ w trakcie projektowania instancji roboczych nie było systemu modułów, interfejs API za wczytywanie kodu do instancji roboczej, a tworzenie skryptów pozostaje podobne do skryptu synchronicznego. popularne w 2009 r. metody wczytywania.

Historia: klasyczne instancje robocze

Konstruktor Worker przyjmuje klasyczną wersję skrypt, który jest w stosunku do adresu URL dokumentu. Natychmiast zwraca odwołanie do nowej instancji roboczej, który ujawnia interfejs przesyłania wiadomości oraz metodę terminate(), która natychmiast się zatrzymuje niszczy pracownika.

const worker = new Worker('worker.js');

W środowiskach roboczych jest dostępna funkcja importScripts() do wczytywania dodatkowego kodu, ale wstrzymuje wykonanie instancji roboczej w celu pobrania i oceny każdego skryptu. Wykonuje też skrypty w zakresie globalnym, jak klasyczny tag <script>. Oznacza to, że zmienne w jednym skrypcie mogą być zastąpione przez zmienne w innej.

worker.js:

importScripts('greet.js');
// ^ could block for seconds
addEventListener('message', e => {
  postMessage(sayHello());
});

greet.js:

// global to the whole worker
function sayHello() {
  return 'world';
}

Z tego powodu pracownicy firm internetowych w przeszłości wywierali ogromny wpływ na architekturę aplikacji. Programiści musieli opracować sprytne narzędzia i sposoby obejścia, aby umożliwić pracodawców internetowych bez rezygnowania z nowoczesnych rozwiązań programistycznych. Firmy tworzące pakiet SDK, webpack umieść w wygenerowanym kodzie, który używa pakietu importScripts(), wdrożenia małego modułu ładującego. dla wczytywania kodu, ale umieszcza moduły w funkcjach, aby uniknąć kolizji zmiennych i symulować eksporty i importy zależności.

Wpisz instancje robocze modułu

Nowy tryb dla pracowników sieci z ergonomią i korzyściami w zakresie wydajności znanymi z JavaScriptu moduły są dostępne w Chrome 80, nazywane zasobami roboczymi modułów. Konstruktor Worker akceptuje teraz nową opcję {type:"module"}, która zmienia wczytywanie skryptu i zgodnie z zapytaniem <script type="module">.

const worker = new Worker('worker.js', {
  type: 'module'
});

Moduły robocze są standardowymi modułami JavaScript, więc mogą używać instrukcji importowania i eksportowania. Jako ze wszystkimi modułami JavaScript zależności są wykonywane tylko raz w danym kontekście (wątek główny, instancji roboczej itp.), a wszystkie przyszłe importy odwołują się do uruchomionej już instancji modułu. Ładowanie a wykonywanie modułów JavaScript jest optymalizowane przez przeglądarki. Zależności modułu mogą być ładowane przed uruchomieniem modułu, co umożliwia załadowanie całych drzew modułów równoległe. Ładowanie modułu również buforuje przeanalizowany kod, co oznacza, że moduły używane na stronie głównej i w instancji roboczej wystarczy przeanalizować tylko raz.

Przejście na moduły JavaScript pozwala też na zastosowanie dynamicznych tagów importuj, by użyć leniwego ładowania kodu bez blokowania wykonywania z innego powodu. Import dynamiczny jest dużo bardziej wyraźny niż używanie interfejsu importScripts() do wczytywania zależności. bo zwracane są eksporty z zaimportowanego modułu, a nie zmienne globalne.

worker.js:

import { sayHello } from './greet.js';
addEventListener('message', e => {
  postMessage(sayHello());
});

greet.js:

import greetings from './data.js';
export function sayHello() {
  return greetings.hello;
}

Aby zapewnić wysoką skuteczność, stara metoda importScripts() nie jest dostępna w tym module pracowników. Przełączenie instancji roboczych na moduły JavaScript oznacza, że cały kod jest wczytywany rygorystycznie . Inny zauważalna zmiana polega na tym, że wartość this w zakresie najwyższego poziomu modułu JavaScript wynosi undefined, podczas gdy w klasycznych instancjach roboczych wartość to zakres globalny instancji. Na szczęście jest zawsze wartością globalną self, która zapewnia odniesienie do zakresu globalnego. Jest dostępny w we wszystkich typach instancji roboczych, w tym mechanizmach Service Worker, a także w DOM.

Wstępnie wczytuj instancje robocze za pomocą: modulepreload

Istotnym wzrostem wydajności, który zapewnia instancje robocze modułów, jest możliwość wstępnego wczytywania i ich zależnościami. Instancje robocze modułu są wczytywane i uruchamiane w standardowy sposób. Moduły JavaScript, co oznacza, że można je wstępnie wczytywać, a nawet przeanalizować za pomocą modulepreload:

<!-- preloads worker.js and its dependencies: -->
<link rel="modulepreload" href="worker.js">

<script>
  addEventListener('load', () => {
    // our worker code is likely already parsed and ready to execute!
    const worker = new Worker('worker.js', { type: 'module' });
  });
</script>

Wstępnie załadowane moduły mogą być używane zarówno przez instancje robocze wątku głównego, jak i moduły. Przydaje się to w przypadku które są importowane w obu kontekstach lub w przypadku, gdy nie można wcześniej o tym wiedzieć czy moduł będzie używany w wątku głównym, czy w instancji roboczej.

Wcześniej opcje dostępne do wstępnego wczytywania skryptów Web Worker były ograniczone i nie i niezawodna. Klasyczni pracownicy mieli własny „moduł” typ zasobu do wstępnego wczytywania, ale nie ma Implementacja funkcji <link rel="preload" as="worker"> w przeglądarkach Z tego powodu podstawową metodą było możliwe użycie interfejsu <link rel="prefetch">, który polegał wyłącznie na tym, w pamięci podręcznej HTTP. W połączeniu z poprawnymi nagłówkami pamięci podręcznej aby uniknąć oczekiwania na pobranie skryptu instancji roboczej. Jednak w przeciwieństwie do modulepreload ta technika nie obsługiwała wstępnego wczytywania zależności ani przygotowania analizy.

A co z współdzielonymi pracownikami?

Udostępnione zasoby robocze mają została zaktualizowana o obsługę modułów JavaScript w Chrome 83. Tak jak dedykowani pracownicy, utworzenie udostępnionej instancji roboczej przy użyciu opcji {type:"module"} wczytuje teraz skrypt jako z modułu zamiast klasycznego skryptu:

const worker = new SharedWorker('/worker.js', {
  type: 'module'
});

Przed obsługą modułów JavaScript konstruktor SharedWorker() wymagał jedynie Adres URL i opcjonalny argument name. Nadal będzie to działać w przypadku użycia klasycznych współdzielonych instancji roboczych. jednak tworzenie współdzielonych instancji roboczych modułu wymaga użycia nowego argumentu options. Dostępne opcje są takie same jak w przypadku dedykowanego pracownika, w tym opcja name, która zastępuje funkcję poprzedni argument name.

A co z skryptem service worker?

Specyfikacja mechanizmu Service Worker została już zaktualizowany tak, by obsługiwała akceptowanie JavaScript jako punktu wejścia przy użyciu tej samej opcji {type:"module"} co instancje robocze modułu, jednak ta zmiana nie została jeszcze wprowadzona w przeglądarkach. Gdy to się stanie, będzie można aby utworzyć instancję service worker, używając modułu JavaScriptu przy użyciu tego kodu:

navigator.serviceWorker.register('/sw.js', {
  type: 'module'
});

Po zaktualizowaniu specyfikacji przeglądarki zaczynają wdrażać nowy sposób działania. Jest to czasochłonne, bo wiąże się to z dodatkowymi komplikacjami do skryptu service worker. Rejestracja skryptu service worker musi porównywać zaimportowane skrypty z ich poprzednimi wersjami w pamięci podręcznej co ma na celu określenie, czy ma zostać zainicjowana aktualizacja, co trzeba zaimplementować w modułach JavaScript. . Skrypty service worker muszą też mieć możliwość omijania pamięci podręcznej dla skryptów w określonych przypadkach, sprawdzanie dostępności aktualizacji.

Dodatkowe materiały i dalsze materiały