Mozilla Hacks: Funkcja calc() z CSS3 w Firefoksie 4

W tym odcinku tłumaczeń artykułów z Mozilla Hacks – notka Paula Rougeta o calc() w CSS. Zarówno oryginalny artykuł, jak i to tłumaczenie dostępne są na licencji Creative Commons – Attribution 3.0.

Funkcja calc() z CSS3 w Firefoksie 4

Poniżej omówiona została funkcja calc() z CSS3. Firefox jej jeszcze nie obsługuje, ale trwają prace nad jej implementacją.

Firefox będzie obsługiwał wartość calc() w CSS (na etapie eksperymentalnym jako -moz-calc() – przyp. tłum.), pozwalającą wyliczyć wymiary danego elementu jako wynik wyrażenia arytmetycznego. Przyda się to przy określaniu wymiarów div-ów, wielkości marginesów, obramowań itp.

Poniżej układ, którego zakodowanie bez użycia funkcji calc() wymagałoby sporo akrobacji (lub użycia JavaScriptu – przyp. tłum.):

/*
* Dwa divy obok siebie, oddzielone marginesem o szerokości 1em
*/
#a {
  width:75%;
  margin-right: 1em;
}
#b {
  width: -moz-calc(25% - 1em);
}

W poniższym przykładzie zadbamy o to, żeby pole tekstowe nie pokrywało się ze swym elementem nadrzędnym:

input {
  padding:2px;
  border:1px solid black;
  display:block;
  width: -moz-calc(100% - 2 * 3px);
}

Jedną z bardziej przydatnych możliwości, jakie daje nam funkcja calc(), jest łączenie wartości w różnych jednostkach:

width: -moz-calc(3px + 50%/3 - 3em + 1rem);

Obecna implementacja obsługuje operatory: +, -, *, /, mod, min i max.

Będziemy również obsługiwać funkcje min() i max(), które można będzie wykorzystać na przykład tak:

div {
  height: -moz-min(36pt, 2em);
  width: -moz-max(50%, 18px);
}

Więcej informacji:

Mozilla Hacks: Grupowanie selektorów za pomocą -moz-any()

Poniższa notka pierwotnie pojawiła się w blogu Davida Barona (a następnie w Mozilla Hacks). Jak większość treści z MozHacks, zarówno oryginalny artykuł, jak i poniższe tłumaczenie dostępne są na warunkach licencji Creative Commons Attribution 3.0 USA.

Funkcjonalność opisana poniżej pojawiła się w Mozilla Central (trunku) i obecnie dostępna jest tylko w conocnych testowych kompilacjach Firefoksa.

Grupowanie selektorów za pomocą -moz-any() w Firefoksie 4

Ostatniej nocy (tj. 23 kwietnia – przyp. tłum.) włączyłem do hg obsługę grupowania selektorów przy użyciu :-moz-any(). Umożliwia to stosowanie alternatywy pomiędzy kombinatorami, dzięki czemu nie trzeba dla każdego różniącego się kawałka selektora powtarzać wielokrotnie podobnych selektorów („any” po angielsku oznacza „dowolny”, tak więc zapis :-moz-any(ol, dir, ul) można czytać jako „dowolny spośród ol, dir i ul” – przyp. tłum.). Obsługa :-moz-any() pozwoliła nam na przykład zamianę tej regułki we wbudowanym w przeglądarkę domyślnym arkuszu stylów:

/* potrójnie (lub bardziej) zagnieżdżone listy nieuporządkowane
   wyświetają prostokąt przy każdym elemencie */
ol ol ul,     ol ul ul,     ol menu ul,     ol dir ul,
ol ol menu,   ol ul menu,   ol menu menu,   ol dir menu,
ol ol dir,    ol ul dir,    ol menu dir,    ol dir dir,
ul ol ul,     ul ul ul,     ul menu ul,     ul dir ul,
ul ol menu,   ul ul menu,   ul menu menu,   ul dir menu,
ul ol dir,    ul ul dir,    ul menu dir,    ul dir dir,
menu ol ul,   menu ul ul,   menu menu ul,   menu dir ul,
menu ol menu, menu ul menu, menu menu menu, menu dir menu,
menu ol dir,  menu ul dir,  menu menu dir,  menu dir dir,
dir ol ul,    dir ul ul,    dir menu ul,    dir dir ul,
dir ol menu,  dir ul menu,  dir menu menu,  dir dir menu,
dir ol dir,   dir ul dir,   dir menu dir,   dir dir dir {
  list-style-type: square;
}

na taką:

/* potrójnie (lub bardziej) zagnieżdżone listy nieuporządkowane
   wyświetają prostokąt przy każdym elemencie */
:-moz-any(ol, ul, menu, dir) :-moz-any(ol, ul, menu, dir) ul,
:-moz-any(ol, ul, menu, dir) :-moz-any(ol, ul, menu, dir) menu,
:-moz-any(ol, ul, menu, dir) :-moz-any(ol, ul, menu, dir) dir {
  list-style-type: square;
}

Teoretycznie można to nawet uprościć do postaci:

/* potrójnie (lub bardziej) zagnieżdżone listy nieuporządkowane
   wyświetają prostokąt przy każdym elemencie */
:-moz-any(ol, ul, menu, dir) :-moz-any(ol, ul, menu, dir)
    :-moz-any(ul, menu, dir) {
  list-style-type: square;
}

…ale działałoby to wolniej, jako że taka regułka nie wpadłaby do kubełka ze znacznikami. (Jeśli :-moz-any() stanie się popularne, możemy dodać dodatkowy kod, dzięki któremu będzie to równie szybkie, ale na razie tego nie zrobiliśmy).

:-moz-any() może zawierać selektory zawierające wiele prostych selektorów (zgodnie z definicją prostych selektorów ze specyfkacji CSS 3 Selectors, w odróżnieniu od CSS 2.1), ale nie może zawierać kombinatorów ani pseudoelementów. Można zatem napisać: :-moz-any(p.warning.new, p.error, div#topnotice) albo :-moz-any(:link, :visited).external:-moz-any(:active, :focus), ale nie można wstawić div p ani div > p czy też :first-letter wewnątrz :-moz-any().

Przedrostek -moz- jest tu obecny z dwóch powodów. Po pierwsze, to tylko propozycja i nie znalazła się jeszcze w żadnej specyfikacji. Po drugie, nie jest to jeszcze kompletna konstrukcja, ponieważ na razie nie obsługuje poprawnie specyficzności.

Ciekawostka: -moz-any() przyda się w kontekście sekcji i nagłówków w HTML 5. Jako że elementy section, article, aside i nav mogą być zafnieżdżane, stylowanie wszystkich elementów h1 na różnych poziomach zagnieżdżenia może być – przy dotychczasowej składni – poważnie skomplikowane.

/* Poziom 0 */
h1 {
  font-size: 30px;
}
/* Poziom 1 */
section h1, article h1, aside h1, nav h1 {
  font-size: 25px;
}
/* Poziom 2 */
section section h1, section article h1, section aside h1, section nav h1,
article section h1, article article h1, article aside h1, article nav h1,
aside section h1, aside article h1, aside aside h1, aside nav h1,
nav section h1, nav article h1, nav aside h1, nav nav h1, {
  font-size: 20px;
}
/* Poziom 3 */
/* ...nawet o tym nie myśl! */

Zastosowanie -moz-any() znacznie upraszcza ten kod:

/* Poziom 0 */
h1 {
  font-size: 30px;
}
/* Poziom 1 */
-moz-any(section, article, aside, nav) h1 {
  font-size: 25px;
}
/* Poziom 2 */
-moz-any(section, article, aside, nav)
-moz-any(section, article, aside, nav) h1 {
  font-size: 20px;
}
/* Poziom 3 */
-moz-any(section, article, aside, nav)
-moz-any(section, article, aside, nav)
-moz-any(section, article, aside, nav) h1 {
  font-size: 15px;
}

Mozilla Hacks: Firefox 4 – nowy parser HTML5

W ramach serii tłumaczeń artykułów z bloga Mozilla Hacks, przedstawiam dzisiaj tłumaczenie artykułu Firefox 4: the HTML5 parser – inline SVG, speed and more. Oryginalny artykuł i jego tłumaczenie dostępne są na warunkach licencji Creative Commons Attribution 3.0 USA.


Autorem wersji oryginalnej tej notki jest Henri Sivonen, zajmujący się nowym parserem HTML5 Firefoksa. Parser HTML jest jednym z najbardziej skomplikowanych i wrażliwych elementów przeglądarki. Steruje procesem przetwarzania kodu źródłowego HTML do stron WWW, dlatego też zmiany w nim wprowadzane są rzadko i muszą być dobrze przetestowane. Podczas gdy większa część silnika Gecko została przebudowana od czasów jego powstania w późnych latach 90., parser był jedną z rzeczy, które pozostały od samego początku niezmienne. Teraz kod ten został zastąpiony nowym, który jest szybszy, zgodny z nowym standardem HTML5 i daje sporo nowych możliwości.

Od jakiegoś czasu prowadziliśmy prace nad zastąpieniem starego parsera HTML w silniku Gecko. Nowy parser został ostatnio domyślnie włączony w trunku, tak więc wystarczy po prostu pobrać conocną kompilację przeglądarki, by wypróbować nowy parser bez potrzeby przełączania żadnych opcji konfiguracyjnych.

Cztery najważniejsze rzeczy, które są związane z nowym parserem HTML5:

  • można korzystać z kodu SVG i MathML bezpośrednio (inline) na stronach HTML5, bez przestrzeni nazw XML
  • przetwarzanie odbywa się poza głównym wątkiem interfejsu użytkownika Firefoksa, zwiększając całkowitą responsywność przeglądarki
  • szybkość zapisu własności innerHTML jest o około 20% większa
  • nowy parser naprawia dziesiątki starych błędów dotyczących przetwarzania.

Obejrzyj democonocnej kompilacji Firefoksa lub innej przeglądarce obsługującej HTML5. Powinno to wyglądać tak:

Co to jest?

Parser HTML5 w Gecko zamienia strumień bajtów w drzewo DOM, zgodnie z algorytmem przetwarzania HTML5.

HTML5 to pierwsza specyfikacja, która szczegółowo określa sposób parsowania HTML. Dotychczasowe specyfikacje nie opisywały, jak przekształcić strumień bajtów w drzewo DOM. Teoretycznie HTML przed HTML5 był określony jako aplikacja SGML. Pociągało to za sobą określony związek między kodem
źródłowym poprawnych dokumentów HTML i drzewem DOM. Jednakże przetwarzanie niepoprawnych dokumentów nie było dobrze określone (a strony WWW w większości składają się z niepoprawnego kodu HTML4), a jednocześnie istnieją konstrukcje SGML, które teoretycznie były częścią HTML, ale nie były implementowane w rzeczywistości przez popularne przeglądarki.

Brak właściwej specyfikacji sprawił, że twórcy przeglądarek musieli samemu dojść do tego, jak traktować nieprawidłowe dokumenty, w razie wątpliwości badając działanie innych przeglądarek o największym udziale w rynku (najpierw Mosaic, potem Netscape, potem IE). Powstało w ten sposób wiele niepisanych reguł, ale także wiele różnic w zachowaniu przeglądarek.

Algorytm przetwarzania HTML5 standaryzuje zachowanie, do którego zbiegają się przeglądarki i inne aplikacje konsumujące HTML. Zgodnie z projektem, algorytm przetwarzania HTML5 jest właściwy do przetwarzania istniejących treści HTML, tak więc aplikacje nie muszą dalej wspierać swoich starych parserów, aby móc wyświetlić stare treści. W conocnych kompilacjach Firefoksa parser HTML5 używany jest dla wszystkich treści typu text/html.

Jak inny jest nowy parser?

Algorytm przetwarzania HTML5 ma dwie główne części: tokenizację i budowanie drzewa. Tokenizacja jest procesem rozdzielania strumienia źródłowego do znaczników, tekstu, komentarzy i atrybutów wewnątrz znaczników. Faza budowania drzewa na bazie znaczników i tekstu pomiędzy nimi buduje drzewo DOM. Algorytm tokenizacji w parserze HTML5 bliższy jest temu, co robi Internet Explorer, niż temu, co robiły dotychczasowe wersje Gecko. IE miał przez długi czas dominację na rynku, przez co witryny były głównie testowane pod kątem prawidłowego działania z tokenizerem IE. Proces budowy drzewa natomiast jest zbliżony do dotychczasowego działania silnika WebKit. Spośród głównych silników przeglądarek przed HTML5, WebKit posiadał najbardziej rozsądne rozwiązanie problemu budowania drzewa.

Ponadto, nowy parser przetwarza strumienie poza głównym wątkiem. Tradycyjnie przeglądarki wykonywały większość zadań w głównym wątku. Radykalne zmiany, takie jak przeniesienie przetwarzania poza główny wątek, uprościły kod parsera HTML5 w porównaniu do starego parsera HTML Gecko.

Co nowego dla twórców witryn?

Zmiany omówione powyżej są interesujące głównie dla programistów przeglądarek. Kluczową cechą parsera HTML5 jest to, że właściwie nie widać, żeby cokolwiek się zmieniło.

Jest jednak jedna duża zmiana istotna dla twórców witryn: kod MathML i SVG bezpośrednio w dokumentach HTML5. Nowy sposób przetwarzania HTML5 uwalnia MathML i SVG od XML-a i udostępnia je w głównym formacie sieci WWW.

Oznacza to, że można osadzać złożone typograficznie wyrażenia matematyczne w dokumentach HTML, bez potrzeby przepisywania całego dokumentu do XHTML, jak też, co ważniejsze, bez potrzeby modyfikacji oprogramowania budującego witrynę do zwracania prawidłowo sformowanego kodu XHTML. Na przykład, aby osadzić w kodzie HTML wzór na rozwiązanie równania kwadratowego, wystarczy tylko poniższy kod:

<math>
  <mi>x</mi>
 
  <mo>=</mo>
  <mfrac>
    <mrow>
      <mo>&minus;</mo>
      <mi>b</mi>
      <mo>&PlusMinus;</mo>
      <msqrt>
        <msup>
 
          <mi>b</mi>
          <mn>2</mn>
        </msup>
        <mo>&minus;</mo>
        <mn>4</mn>
        <mo>&InvisibleTimes;</mo>
        <mi>a</mi>
 
        <mo>&InvisibleTimes;</mo>
        <mi>c</mi>
      </msqrt>
    </mrow>
    <mrow>
      <mn>2</mn>
      <mo>&InvisibleTimes;</mo>
      <mi>a</mi>
 
    </mrow>
  </mfrac>
</math>

W podobny sposób można osadzić skalowalną grafikę SVG – bez przepisywania HTML-a do XHTML-a. Podczas gdy rozmiary ekranów i rozdzielczości stają się coraz bardziej zróżnicowane, coraz większą wagę przywiązuje się do jakości grafiki przy różnych stopniach powiększenia. Wprawdzie dotychczas można było używać grafiki SVG w dokumentach HTML przez referencję (używając elementu object), wstawianie SVG bezpośrednio jest w niektórych przypadkach wygodniejsze. Na przykład ikona ostrzeżenia może być teraz osadzona bezpośrednio, a nie ładowana z zewnętrznego pliku.

<svg height=86 width=90 viewBox='5 9 90 86' style='float: right;'>
  <path stroke=#F53F0C stroke-width=10 fill=#F5C60C stroke-linejoin=round d='M 10,90 L 90,90 L 50,14 Z'/>
  <line stroke=black stroke-width=10 stroke-linecap=round x1=50 x2=50 y1=45 y2=75 />
</svg>

Wystarczy utworzyć stronę zaczynającą się od <!DOCTYPE html> i wstawić do niej dwa powyższe fragmenty, a będzie to działać w nowych nocnych kompilacjach Firefoksa.

Ogólnie, jeśli mamy kod MathML czy SVG jako XML, wystarczy po prostu wkleić kod XML bezpośrednio do HTML-a (pomijając, jeśli istnieją, deklarację XML oraz doctype). Dwa zastrzeżenia: kod nie może stosować przedrostków przestrzeni nazw dla elementów (tak więc żadnych svg:svg ani math:math), a przedrostkiem przestrzeni nazw XLink musi być xlink.

W powyższych fragmentach MathML i SVG uważna osoba dostrzeże, że bezpośrednio osadzane fragmenty MathML i SVG są bardziej podobne do HTML i prostsze niż po prostu wklekony w to miejsce XML. Nie ma deklaracji przestrzeni nazw, a niepotrzebne cudzysłowy wokół wartości atrybutów zostały pominięte. Działa to, ponieważ znaczniki są tokenizowane przez tokenizer HTML5, a nie przez tokenizer XML. Pomijanie deklaracji przestrzeni nazw działa, ponieważ proces budowania drzewa nie używa atrybutów wyglądających jak deklaracje przestrzeni nazw do przypisania elementom “MathML-owatości” czy “SVG-owatości”. Zamiast tego <svg> rozpoczyna zasięg elementów, którym przypisana będzie przestrzeń nazw SVG w DOM, a <math> rozpoczyna zasięg elementów, którym w DOM przypisana będzie przestrzeń nazw MathML. W przykładzie MathML widać także, że stosowane są odwołania do encji nazwanych, które wcześniej nie były obsługiwane w HTML.

Poniżej krótkie podsumowanie przetwarzania MathML i SVG bezpośrednio w dokumentach HTML dla twórców witryn:

  • <svg></svg> przypisany jest do przestrzeni nazw SVG w DOM.
  • <math></math> przypisany jest do przestrzeni nazw MathML w DOM.
  • foreignObject oraz annotation-xml (na różnych mniej istotnych elementach) rozpoczyna zagnieżdżony zasięg HTML, tak więc można zagnieżdżać SVG, MathML i HTML w sposób, jakiego można oczekiwać.
  • Parser poprawia wielkość znaków w znakowaniu, tak więc <SVG VIEWBOX=’0 0 10 10′> działa w kodzie HTML.
  • Metody DOM i selektory CSS rozróżniają wielkość znaków, należy więc pisać wywołania DOM oraz selektory CSS z użyciem kanonicznej wielkości znaków, czyli tzw. camelCase w przypadku różnych części SVG, takich jak viewBox.
  • Składnia <foo/> otwiera i natychmiast zamyka element foo, jeśli jest to element MathML lub SVG (ale nie element HTML).
  • Tokenizacja atrybutów przebiega w taki sam sposób, jak w HTML, tak więc można pomijać cudzysłowy w sytuacjach, w których można byłoby je pominąć w HTML (tj. kiedy wartość atrybutu nie jest pusta oraz nie zawiera białych znaków, ", ', `, <, = ani >).
  • Uwaga: dwie powyższe cechy nie współgrają ze sobą dobrze z przyczyn zachowania zgodności z tokenizacją starego HTML-a. Jeśli cudzysłowy zostaną pominięte przy wartości ostatniego atrybutu, konieczne jest wstawienie spacji przed zamykającym ukośnikiem. Tak więc prawidłowy jest kod: <circle fill=green />, ale nieprawidłowy jest: <circle fill=red/>.
  • Atrybuty zaczynające się od xmlns nie mają absolutnie żadnych skutków, nie wpływają na obecność elementów ani atrybutów w danej przestrzeni nazw, tak więc nie ma potrzeby ich stosowania.
  • Atrybuty w przestrzeni nazw XLink muszą stosować przedrostek xlink (np. xlink:href).
  • Nazwy elementów nie mogą zawierać przedrostków ani dwukropków.
  • Zawartość elementów script w SVG tokenizowana jest tak, jak w XML – nie jak zawartość elementów script w HTML.
  • Kiedy element SVG lub MathML jest otwarty, sekcje <![CDATA[]]> działają tak, jak w XML. Można to wykorzystać do ukrycia treści tekstowych przed starszymi przeglądarkami nie obsługującymi SVG ani MathML w text/html.
  • Encje nazwane MathML dostępne są w całym dokumencie (także w treści HTML).
  • Aby zapewnić działanie stron, na których autorzy umieścili fragmenty SVG w HTML (nie wiadomo, po co) lub użyli znacznika <math> do celów niezwiązanych z MathML, próby zagnieżdżenia różnych popularnych elementów HTML jako elementów potomnych SVG (bez użycia foreignObject) spowodują natychmiastowe wyjście z kontekstu SVG lub MathML. Może to sprawić, że literówki będą miały zaskakujące efekty.

Mozilla Hacks: Korzystanie z Web Workers

W ramach serii tłumaczeń artykułów z bloga Mozilla Hacks, przedstawiam dzisiaj tłumaczenie artykułu Using Web Workers – Working Smarter, Not Harder. Oryginalny artykuł i jego tłumaczenie dostępne są na warunkach licencji Creative Commons Attribution 3.0 USA.


Korzystanie z Web Workers – jak pracować mądrze, a nie ciężko

Autorem tego artykułu jest Malte Ubl, który wykonał sporo dobrej roboty przy użyciu technologii Web Workers w ramach projektu bespin.

W ostatnim czasie aplikacje webowe stały się znacznie bogatsze. Programy działające w przeglądarce, takie jak GMail, Meebo i Bespin pokazują nam, jak WWW będzie wyglądać i zachowywać się w przyszłości. Kluczowym aspektem tworzenia przyjaznej dla użytkownika aplikacji jest wysoka responsywność. Użytkownicy nie lubią czekać, a szczególnie nie lubią sytuacji, w których wydaje się, że program działa, po czym przestaje reagować.

W sercu współczesnych aplikacji WWW po stronie klienta leży język programowania JavaScript. JavaScript wraz z obiektowym modelem dokumentu DOM jest całkowicie jednowątkowy. Oznacza to, że w JavaScripcie tylko jedna rzecz może się zdarzyć w danej chwili. Nawet jeśli komputer ma 32-rdzeniowy procesor, tylko jeden rdzeń będzie zajęty podczas długich obliczeń. Na przykład, obliczając idealną trajektorię lotu na Księżyc nie można jednocześnie renderować animacji, która pokazuje trajektorię w tym samym czasie i nie ma możliwości reagowania na zdarzenia użytkownika, takie jak kliknięcia myszą czy pisanie na klawiaturze podczas obliczeń.

Współbieżność

Dla zachowania responsywności podczas intensywnych obliczeń większość współczesnych języków programowania wykorzystuje współbieżność. W przeszłości do uzyskania współbieżności często wykorzystywano wątki. Klasyczne wątki utrudniają jednakże programiście zrozumienie przebiegu programu, co często prowadzi do trudnych do zrozumienia błędów i chaotycznego zachowania w chwili, gdy różne wątki próbują równocześnie operować na tych samych danych.

Technologia wątków roboczych – Web Workers – zalecana przez WHATWG, pojawiła się w Firefoksie 3.5, pozwalając na wzbogacenie programów w JavaScripcie o współbieżność, unikając problemów związanych z typowymi programami wielowątkowymi. Rozpoczęcie wątku roboczego jest bardzo proste – wystarczy użyć konstruktora new Worker.

W tym przykładzie plik worker.js zostanie wczytany i uruchomiony w nowym wątku, dla niego utworzonym.

// Utwórz wątek roboczy na bazie pliku "worker.js"
var worker = new Worker("worker.js");

Komunikacja między głównym wątkiem interfejsu użytkownika a wątkami roboczymi opiera się na przesyłaniu komunikatów przy użyciu metody postMessage. Metoda ta została dodana do Firefoksa 3 by zapewnić komunikację między oknami. Aby przesłać komunikat z wątku roboczego do strony, wystarczy użyć tej metody:

// Wyślij komunikat do głównego wątku UI
postMessage("Witaj, strono!");

Aby odebrać wiadomość od wątku roboczego, na obiekcie typu Worker należy określić procedurę obsługi zdarzenia “onmessage”. Tutaj po prostu wyświetlimy dane zdarzenia, przekazane do tej procedury. W naszym przypadku własność “event.data” zawiera tekst “Witaj, strono!”, przesłany powyżej.

worker.onmessage = function (event) {

  alert(event.data);
  // Wyślij wiadomość do wątku roboczego
  worker.postMessage("Witaj, wątku!");

}

Do wysłania komunikatów do wątku roboczego wywołujemy metodę postMessage na obiekcie wątku. Aby odebrać te komunikaty w wątku, należy zdefiniować funkcję onmessage, która będzie wywoływana po każdym przesłaniu do niego komunikatu.

Obsługa błędów

Istnieją dwa sposoby obsługi błędów czasu wykonania w wątku. Po pierwsze, wewnątrz wątku można zdefiniować funkcję onerror. Po drugie, można obsłużyć błędy z zewnątrz wątku, przypisując procedurę onerror obiektowi typu Worker:

worker.onerror = function (event) {

  alert(event.message);
  event.preventDefault();
}

Metoda event.preventDefault() zapobiega wykonaniu domyślnej operacji, którą byłoby tutaj wyświetlenie błędu użytkownikowi lub wypisanie jej w konsoli błędów. Zamiast tego, w tym miejscu wyświetlmy treść błędu w oknie powiadomienia.

Brak współdzielenia

Wątki robocze nie dzielą żadnych stanów ze stroną, z którą są powiązane, ani z żadnymi innymi wątkami roboczymi; jedyną możliwą interakcją jest przesyłanie komunikatów przy użyciu metody postMessage. Wątki robocze nie mają dostępu do DOM, nie mogą więc manipulować stroną WWW. W ten sposób nie ma żadnego ryzyka dla integralności danych w sytuacji, gdy wiele wątków chce równocześnie operować na tych samych danych.

Typowe wykorzystanie wątków roboczych składa się ze strony-komponentu JavaScript, oczekującej na zdarzenia użytkownika. Kiedy nastąpi zdarzenie wywołujące intensywne obliczenia, do wątku roboczego przesyłany jest komunikat, który powoduje rozpoczęcie obliczeń. Skrypt na stronie może jednak natychmiast powrócić do oczekiwania na dalsze zdarzenia. W momencie, gdy wątek roboczy skończy pracę, przesyła wiadomość do strony, która może wówczas np. wyświetlić wyniki.

nigdy-wiecej
Ostrzeżenie przed nieresponsywnym skryptem, wyświetlane przez przeglądarki, gdy skrypt wykonuje się zbyt długo, odchodzi w przeszłość dzięki wątkom roboczym.

Przykład – ciąg Fibonacciego

W kolejnym przykładzie wątek roboczy oblicza w tle liczby Fibonacciego od 0 do 99. W rzeczywistości, ponieważ obliczanie liczb Fibonacciego tą nieefektywną
metodą może trwać długo przy większych liczbach (np. większych niż 30), skrypt może nawet nigdy nie zakończyć się na komputerze (albo „wyłożyć sie” z powodu przepełnienia stosu), ale ponieważ dzieje się to w wątku roboczym, nie ma to żadnego skutku co do responsywności strony. Można więc nadal wyświetlać skomplikowaną animację, by uprzyjemnić oczekiwanie na kolejną liczbę.

Poniższa strona HTML zawiera skrypt uruchamiający wątek roboczy z pliku „fib-worker.js”. Komunikaty z wątku roboczego wyświetlane są w konsoli przeglądarki (czy raczej Firebuga – przyp. tłum.) przy użyciu console.log.

<!DOCTYPE html>
<html>
    <head>
      <title>Web Worker API Demo</title>
      <script type="text/javascript">

        var worker = new Worker("fib-worker.js");
        worker.onmessage = function (event) {
          console.log(event.data.index +" -> " + event.data.value)
        }
      </script>  
    </head>
    <body>
    </body>

</html>

Plik JavaScript z implementacją wątku roboczego zawiera pętlę, która wylicza liczby Fibonacciego i przesyła wyniki do strony.

// Plik fib-worker.js
function fib(n) {

   return n < 2 ? n : fib(n-1) + fib(n-2);

}
 
for(var i = 0; i < 100; ++i) {

   postMessage({
      index: i,
      value: fib(i)

   })
}

W powyższym przykładzie widać też, że przy użyciu postMessage można przesyłać złożone obiekty. Obiekty takie mogą zawierać wszystko to, co można przesłać w formacie JSON. Oznacza to, że nie można przesłać funkcji, a obiekty przekazywane są przez wartość, a nie przez referencję.

API wątków roboczych

Wątki robocze obsługują funkcję o nazwie importScripts. Dzięki niej można wczytać do wątku roboczego więcej plików źródłowych.

importScripts("file.js");

importScripts("foo.js", "bar.js");

Jeśli funkcja zostanie wywołana z wieloma argumentami, skrypty zostaną pobrane równolegle, ale wykonane w kolejności zdefiniowania. Funkcja ta jest blokująca – bieg wątku zostanie wstrzymany do momentu pobrania i wykonania wszystkich skryptów.

W kolejnym przykładzie wczytujemy zewnętrzny plik JavaScript, który oblicza wartość funkcji SHA-1 z ciągów znaków, a następnie wykorzystujemy go do wyliczania wartości SHA-1 dla zawartości odczytanej poprzez żądanie AJAX. Używamy tu standardowego obiektu XMLHttpRequest do pobrania zawartości z adresu URL przekazanego do zdarzenia onmessage. Co ciekawe, nie musimy budować tutaj asynchronicznego żądania AJAX, jako że wątek sam w sobie jest asynchroniczny względem strony, tak więc oczekiwanie na żądanie HTTP nie jest tu żadnym problemem.

importScripts("sha1.js")
 
function onmessage(event) {
    var xhr = new XMLHttpRequest();

    xhr.open('GET', event.data, false);
    xhr.send();

    postMessage(sha1(xhr.responseText));
}

Inne API dostępne dla wątków roboczych

Wątki mogą używać XMLHttpRequest do żądań AJAX, jak w powyższym przykładzie, jak również mają dostęp do bazy danych po stronie klienta dzięki API Web Storage. API te w wątkach roboczych są takie same jak w „zwykłym” JavaScripcie.

Funkcje setTimeout i setInterval (oraz clearTimeout i clearInterval) są dostępne, co pozwala na wykonywanie kodu po upływie danego czasu lub co pewien interwał. Dostępny jest też obiekt navigator object, udostępniający informacje o przeglądarce.

Dodatkowe API mogą zostać dodane w przyszłości.

Zgodność z przeglądarkami

W momencie pisania tego artykułu, o ile wiadomo jego autorowi, tylko Firefox 3.5 obsługuje możliwość przesyłania złożonych obiektów poprzez postMessage i implementuje rozszerzone API opisane powyżej. Safari 4 zawiera prostą implementację API Worker. W innych przeglądarkach można korzystać z Workers poprzez wtyczkę Google Gears, w której technologia ta pojawiła się na początku.

Przykład wykorzystania na prawdziwej stronie

W ramach projektu Bespin, opartego na przeglądarce edytora kodu źródłowego, z powodzeniem wykorzystujemy wątki robocze do implementacji intensywnych dla procesora funkcji, takich jak sprawdzanie poprawności kodu na bieżąco czy podpowiadanie kodu. Stworzyliśmy także nakładkę, implementującą API Worker w ramach Google Gears, która także dodaje brakującą funkcjonalność w Safari 4, a także umożliwiliśmy korzystanie ze transparentnych zdarzeń własnych, na bazie interfejsu postMessage. Komponenty te zostaną upublicznione jako odrębna biblioteka, do wykorzystania przez inne projekty.

Technologia Web Workers odgrywa istotną rolę w próbie uczynienia z Otwartej Sieci WWW jeszcze potężniejszej platformy dla złożonych programów. Jako że wszystko, co robią wątki robocze, to wykonywanie JavaScriptu, łatwo jest napisać skrypty, które działają także w przeglądarkach nie zapewniających luksusu wątków roboczych. Polecamy dodanie Web Workers do Twoich programów, aby stały się bardziej responsywne i przyjemniejsze w użyciu.