Noțiuni de bază cu Web Workers

Unul dintre multele scopuri de proiectare ale limbajului JavaScript a fost să-l păstreze cu un singur filet și, prin extensie, simplu. Deși trebuie să recunosc că, având în vedere idiosincraziile constructurilor limbajului, nu este altceva decât simplu! Dar ceea ce înțelegem prin a fi "cu un singur filet" este că există doar un fir de control în JavaScript; da, din păcate, motorul dvs. JavaScript poate face un singur lucru la un moment dat.

Acum, nu sunet prea restrictiv pentru a face uz de procesoarele multi-core care stau inactive pe masina ta? HTML5 promite să schimbe toate acestea.


Modelul JavaScript cu un singur filet

Lucrătorii web trăiesc într-o lume restricționată, fără acces DOM, deoarece DOM nu este sigură în legătură cu firul.

O școală de gândire consideră natura JavaScript cu o singură fire ca o simplificare, dar cealaltă o respinge ca o limitare. Acest din urmă grup are un punct foarte bun, mai ales atunci când aplicațiile web moderne folosesc greu JavaScript pentru a gestiona evenimentele UI, interogarea sau interogarea API-urilor de pe server, procesarea unor cantități mari de date și manipularea DOM pe baza răspunsului serverului.

A fi capabil sa faca atat de mult intr-un singur fir de control in timp ce mentine un interfata responsabila este deseori o sarcina descurajatoare si ii obliga pe dezvoltatori sa recurga la hacks si solutii (cum ar fi folosirea setTimeout (), setInterval (), sau folosind XMLHttpRequest și evenimentele DOM) pentru a obține concurrency. Cu toate acestea, merită remarcat faptul că aceste tehnici oferă cu siguranță o modalitate de a efectua apeluri asincrone, dar non-blocarea nu înseamnă neapărat concomitent. John Resig explică de ce nu puteți rula nimic în paralel pe blogul său.

Limitările

Dacă ați lucrat cu JavaScript pentru o perioadă rezonabilă de timp, este foarte probabil că ați întâmpinat următoarea fereastră de dialog enervant, care spune că un script durează prea mult pentru a fi executat. Da, aproape de fiecare dată când pagina ta nu mai răspunde, motivul poate fi atribuit unui cod JavaScript.

Iată câteva dintre motivele pentru care browserul dvs. ar putea închide cizmele în timp ce execută scriptul dvs.:

  • Administrarea DOM excesivă: Manipularea DOM este probabil cea mai costisitoare operațiune pe care o puteți face cu JavaScript. În consecință, o mulțime de operațiuni de manipulare DOM fac ca scenariul dvs. să fie un bun candidat pentru refactorizare.
  • Bucle fără sfârșit: Nu doare niciodată să scaneze codul pentru bucle complexe imbricate. Acestea tind să facă mult mai mult decât ceea ce este de fapt necesar. Poate că puteți găsi o soluție diferită care oferă aceeași funcționalitate.
  • Combinând cele două: Cel mai rău lucru pe care îl putem face este actualizarea repetată a DOM într-o buclă atunci când există mai multe soluții elegante, cum ar fi utilizarea unui DocumentFragment.

Web Workers la salvare

... non-blocarea nu înseamnă neapărat concomitent ...

Mulțumită HTML5 și Web Workers, acum puteți crea un nou fir - oferind asincronie adevărată. Noul lucrător poate rula în fundal în timp ce firul principal procesează evenimente UI, chiar dacă firul lucrătorului este ocupat pentru procesarea unui volum mare de date. De exemplu, un lucrător ar putea procesa o structură JSON mare pentru a extrage informații valoroase pentru a fi afișate în UI. Dar destul de bâlbâitul meu; Să vedem un cod în acțiune.

Crearea unui lucrător

În mod normal, codul care aparține unui lucrător web se află într-un fișier JavaScript separat. Fire-ul părinte creează un nou lucrător prin specificarea URI-ului fișierului script în Muncitor constructor, care încarcă și execută asincron fișierul JavaScript.

var primeWorker = lucrător nou ("prime.js");

Porniți un muncitor

Pentru a iniția un lucrător, firul părinte trimite un mesaj lucrătorului, după cum urmează:

var curent = $ ('# prime'). attr ('valoare'); primeWorker.postMessage (curent);

Pagina părinte poate comunica cu lucrătorii folosind postMessage API, care este folosit și pentru mesageria de origine transfrontalieră. În afară de trimiterea tipurilor de date primitive către lucrător, postMessage API acceptă, de asemenea, structurile JSON care trec. Cu toate acestea, nu puteți să transmiteți funcții deoarece acestea pot conține referințe la DOM.

Firele părintelui și muncitorului au propriul spațiu separat; mesajele transmise către și înapoi sunt copiate mai degrabă decât partajate.

În spatele scenei, aceste mesaje sunt serializate la muncitor și apoi de-serializate la sfârșitul primitorului. Din acest motiv, este descurajat să trimită lucrătorului cantități imense de date.

Firele părinte pot, de asemenea, să înregistreze un apel de apel pentru a asculta mesajele pe care lucrătorul le returnează în urma efectuării sarcinii. Aceasta permite firului părinte să ia măsurile necesare (cum ar fi actualizarea DOM) după ce lucrătorul și-a jucat rolul. Luați în considerare acest cod:

primeWorker.addEventListener ('mesaj', functie (eveniment) console.log ('primirea de la Worker:' + event.data); $ ('# prime'); html (event.data);

eveniment Obiectul conține două proprietăți importante:

  • ţintă: utilizat pentru identificarea lucrătorului care a trimis mesajul; în primul rând util într-un mediu cu mai mulți lucrători.
  • date: mesajul postat de lucrător înapoi la firul părinte.

Lucrătorul însuși este conținut în prime.js și registre pentru mesaj eveniment, pe care îl primește de la părintele său. De asemenea, utilizează același lucru postMessage API pentru a comunica cu firul părinte.

auto.addEventListener ('mesaj', functie (eveniment) var currPrime = event.data, nextPrime; setInterval (function () nextPrime = getNextPrime (currPrime) );

Lucrătorii web trăiesc într-un mediu restrâns și sigur.

În acest exemplu, găsim pur și simplu următorul cel mai înalt număr și trimiteți în mod repetat rezultatele înapoi la firul părinte, care la rândul său actualizează interfața cu noua valoare. În contextul unui lucrător, ambele de sine și acest se referă la domeniul de aplicare global. Lucrătorul poate adăuga un ascultător de eveniment pentru mesaj eveniment, sau poate defini onmessage handler pentru a asculta mesajele trimise de firul părinte.

Sarcina de a găsi următorul număr prime nu este, evident, cazul ideal pentru un lucrător, dar a fost ales aici pentru a demonstra conceptul de transmitere a mesajelor. Mai târziu, analizăm posibilele și cazurile practice de utilizare, în care utilizarea unui Web Worker ar cuceri cu adevărat beneficii.

Terminarea lucrătorilor

Lucrătorii sunt intensivi din punct de vedere al resurselor; ele sunt fire de nivel OS. Prin urmare, nu doriți să creați un număr mare de fire de lucrători și ar trebui să întrerupeți lucrătorul web după terminarea lucrărilor. Lucrătorii se pot termina, în felul următor:

self.close ();

Sau un fir părinte poate termina un lucrător:

primeWorker.terminate ();

Securitate și restricții

În interiorul unui script de lucrător, nu avem acces la multe obiecte importante ale JavaScript document, fereastră, consolă, mamă și, cel mai important, nici un acces la DOM. Fără acces la DOM și imposibilitatea actualizării paginii pare prea restrictivă, dar este o decizie importantă de proiectare a securității. Imaginați-vă doar ce se poate întâmpla dacă mai multe fire încearcă să actualizeze același element. Astfel, lucrătorii web trăiesc într-un mediu restrâns și sigur.

Acestea fiind spuse, puteți utiliza în continuare muncitorii pentru prelucrarea datelor și returnarea rezultatului înapoi la firul principal, care poate apoi să actualizeze DOM. Deși li se refuză accesul la unele obiecte JavaScript destul de importante, lucrătorilor li se permite să utilizeze anumite funcții, cum ar fi setTimeout () / clearTimeout (), setInterval () / clearInterval (), navigator, etc Puteți utiliza, de asemenea, XMLHttpRequest și localStorage obiecte din interiorul muncitorului.

Aceleași restricții privind originea

În contextul unui lucrător, ambele de sine și acest se referă la domeniul de aplicare global.

Pentru a comunica cu un server, muncitorii trebuie să respecte aceeași politică de origine. De exemplu, un script găzduit pe http://www.example.com/ nu puteți accesa un script pe https://www.example.com/. Chiar dacă numele gazdă sunt aceleași, politica aceluiași-original declară că protocolul trebuie să fie același. În mod normal, aceasta nu este o problemă. Este foarte probabil să scrieți atât muncitorul, clientul, cât și să îi serviți din același domeniu, dar cunoașterea restricției este întotdeauna utilă.

Probleme locale de acces cu Google Chrome

Google Chrome plasează restricții privind accesarea lucrătorilor la nivel local, prin urmare nu veți putea rula aceste exemple într-o configurație locală. Dacă doriți să utilizați Chrome, trebuie să găzduiți aceste fișiere pe un anumit server sau să utilizați --permite-file-acces la fișiere- semnalizați când porniți Chrome din linia de comandă. Pentru OS X, porniți cromul după cum urmează:

$ / Aplicații / Google \ Chrome.app/Contents/MacOS/Google \ Chrome - toate fișierele de acces din fișiere

Cu toate acestea, utilizarea acestui steag nu este recomandată într-un mediu de producție. Astfel, cel mai bun pariu este să găzduiți aceste fișiere pe un server web și să vă testați lucrătorii web în orice browser acceptat.

Depistarea lucrătorilor și depistarea erorilor

Nu are acces la consolă face acest lucru oarecum netrivial, dar datorită instrumentelor pentru dezvoltatori Chrome, se poate depana codul lucrătorului ca și cum ar fi alt cod JavaScript.

Pentru a rezolva orice erori aruncate de către lucrătorii web, puteți asculta eroare eveniment, care găzduiește un obiect ErrorEvent. Puteți examina acest obiect pentru a cunoaște cauza detaliată a erorii.

prime error (error), funcția (eroare) console.log ('Eroare cauzată de lucrător:' + error.filename + 'la numărul liniei:' + error.lineno + 'Mesaj detaliat: + error.message) ;);

Fire de lucru multiple

Deși este obișnuit să existe mai multe fire de lucrători care împart munca între ei, un cuvânt de prudență este în ordine. Specificațiile oficiale specifică faptul că acești lucrători sunt relativ greu și sunt de așteptat să fie scripturi de lungă durată care rulează în fundal. Lucrătorii web nu sunt destinați a fi utilizați în număr mare din cauza costului ridicat al performanței de pornire și a costurilor ridicate de memorie per-instanță.

Prezentare succintă a lucrătorilor partajați

Specificațiile descriu două tipuri de lucrători: dedicate și împărtășite. Până acum, am văzut exemple de lucrători dedicați. Ele sunt legate direct de scriptul creatorului / pagina în sensul că au o relație una cu o singură legătură cu scriptul / pagina care le-a creat. Lucrătorii partajați, pe de altă parte, pot fi împărțiți între toate paginile dintr-o origine (adică: toate paginile sau scripturile de aceeași origine pot comunica cu un lucrător partajat).

Pentru a crea un lucrător partajat, trebuie doar să treci URL-ul scriptului sau numele lucrătorului în constructorul SharedWorker.

Diferența majoră în modul în care sunt folosiți lucrătorii împărțiți este că aceștia sunt asociați cu a port pentru a urmări accesul la scriptul părinte.

Următorul fragment de cod creează un lucrător partajat, înregistrează un apel invers pentru a asculta mesajele postate de lucrător și publică un mesaj către lucrătorul partajat:

var sharedWorker = noul SharedWorker ('findPrime.js'); sharedWorker.port.onmessage = funcție (eveniment) ... sharedWorker.port.postMessage ("datele pe care doriți să le trimiteți");

În mod similar, un lucrător poate asculta pentru conectați eveniment, care este primit atunci când un nou client încearcă să se conecteze la lucrător și apoi trimite un mesaj la acesta în consecință.

onconnect = funcție (eveniment) // event.source conține referința la portul clientului clientPort = event.source; // ascult pentru orice mesaje trimite clientul meu clientPort.onmessage = functie (eveniment) // event.data conține mesajul trimis de client var data = event.data; ... // Post Data după procesarea clientPort.postMessage ('prelucrat date'); ;

Din cauza naturii lor comune, puteți menține aceeași stare în diferite file ale aceleiași aplicații, deoarece atât paginile din file diferite utilizează același script de lucrător partajat pentru a menține și raporta starea. Pentru mai multe detalii despre lucrătorii împărțiți, vă încurajez să citiți specificațiile.


Cazuri practice de utilizare

Lucrătorii web nu sunt destinați a fi utilizați în număr mare din cauza costului ridicat al performanței de pornire și a costurilor ridicate de memorie per-instanță.

Un scenariu din viața reală ar putea fi atunci când sunteți forțat să vă ocupați de un API terță parte sincronă, care obligă firul principal să aștepte un rezultat înainte de a trece la următoarea declarație. Într-un astfel de caz, puteți delega această sarcină unui lucrător nou-născut pentru a mobiliza capacitatea asincronă în beneficiul dvs..

De asemenea, lucrătorii web excelează în situațiile de votare în care puteți să căutați în mod continuu o destinație în fundal și să trimiteți un mesaj către firul principal când sosesc date noi.

Este posibil să fie necesar să procesați o cantitate imensă de date returnate de server. În mod tradițional, procesarea unei mulțimi de date influențează negativ reacția aplicației, făcând astfel experiența utilizatorului inacceptabilă. O soluție mai elegantă ar împărți munca de prelucrare între mai mulți lucrători pentru a procesa porțiuni care nu se suprapun de date.

Alte cazuri de utilizare ar putea fi analizarea surselor video sau audio cu ajutorul mai multor lucrători web, fiecare lucrând pe o parte predefinită a problemei.


Concluzie

Imaginați-vă puterea asociată cu fire multiple într-un alt mediu filetat.

Ca și în multe lucruri din spec. HTML5, spec. Lucrătorului web continuă să evolueze. Dacă aveți de gând să lucrătorii web, nu va face rău pentru a da spec.

Suportul pentru cross-browser este destul de bun pentru angajații dedicați cu versiunile curente de Chrome, Safari și Firefox. Chiar si IE nu ramane prea departe in urma cu IE10 care se ocupa. Cu toate acestea, lucrătorii partajați sunt sprijiniți numai pe versiunile curente de Chrome și Safari. Surprinzător, cea mai recentă versiune a browserului Android disponibil în Android 4.0 nu suportă lucrătorii web, deși au fost acceptate în versiunea 2.1. Apple a inclus, de asemenea, suport pentru lucrătorii web începând cu iOS 5.0.

Imaginați-vă puterea asociată cu fire multiple într-un alt mediu filetat. Posibilitățile sunt nesfârșite!

Cod