aboutsummaryrefslogtreecommitdiff
path: root/files/pl/web/javascript/eventloop/index.html
blob: e61f3c2a48b20870aab16d3b2846cffcacb076d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
---
title: Model współbieżności i Event Loop
slug: Web/JavaScript/EventLoop
translation_of: Web/JavaScript/EventLoop
---
<div>{{JsSidebar("Advanced")}}</div>

<p>Model współbieżności w JavaScript opiera się o "event loop". Model ten jest lekko odmienny od spotykanego innych językach programowania takich jak C lub Java.</p>

<h2 id="Koncepcje_środowiska_wykonawczego">Koncepcje środowiska wykonawczego</h2>

<p>Poniższa sekcja objaśnia model teoretyczny. Nowoczesne silniki JavaScript implementują i optymalizują w dużej mierze opisaną semantykę.</p>

<h3 id="Reprezentacja_wizualna">Reprezentacja wizualna</h3>

<p style="text-align: center;"><img alt="Stack, heap, queue" src="/files/4617/default.svg" style="height: 270px; width: 294px;"></p>

<h3 id="Stack">Stack</h3>

<p>Wywołania funkcji formują stos (ang. stack) <em>klatek</em>.</p>

<pre class="brush: js line-numbers  language-js"><code class="language-js"><span class="keyword token">function</span> <span class="function token">foo</span><span class="punctuation token">(</span>b<span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="keyword token">var</span> a <span class="operator token">=</span> <span class="number token">10</span><span class="punctuation token">;</span>
  <span class="keyword token">return</span> a <span class="operator token">+</span> b <span class="operator token">+</span> <span class="number token">11</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

<span class="keyword token">function</span> <span class="function token">bar</span><span class="punctuation token">(</span>x<span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="keyword token">var</span> y <span class="operator token">=</span> <span class="number token">3</span><span class="punctuation token">;</span>
  <span class="keyword token">return</span> <span class="function token">foo</span><span class="punctuation token">(</span>x <span class="operator token">*</span> y<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>

console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="function token">bar</span><span class="punctuation token">(</span><span class="number token">7</span><span class="punctuation token">)</span><span class="punctuation token">)</span><span class="punctuation token">;</span> <span class="comment token">//zwraca 42</span></code></pre>

<p>Podczas wywoływania funkcji <code>bar</code>, tworzona jest pierwsza klatka (ang. frame), która zawiera argumenty funkcji <code>bar</code> oraz jej lokalne zmienne. Gdy <code>bar</code> wywołuje <code>foo</code>, tworzona jest kolejna klatka, która trafia na wierzch stosu. Druga klatka zawiera argumenty funkcji  <code>foo</code> dla której została utworzona oraz jej lokalne zmienne. Gdy funkcja <code>foo</code> zwraca wynik działania, klatka znajdująca się na wierzchu stosu jest usuwana (pozostawiając wyłącznie klatkę wywołującą funkcję <code>bar</code>). Gdy w kolejnym kroku funkcja <code>bar</code> zwróci wynik, stos zostaje opróżniony.</p>

<h3 id="Heap">Heap</h3>

<p>Sterta (ang. heap) jest pojęciem opisującym duży nieuporządkowany obszar pamięci. W nim przechowywane są obiekty.</p>

<h3 id="Queue">Queue</h3>

<p>Środowisko wykonawcze (ang. runtime) JavaScript zawiera kolejkę wiadomości (ang. queue), która stanowi listę komunikatów do przetworzenia. Każda z wiadomości posiada przyporządkowaną funkcję, która przechowuje instrukcję przetworzenia danej wiadomości.</p>

<p>W przeciwieństwie do stosu wiadomości są przetwarzane począwszy od najstarszej z nich (znajdującej się na początku kolejki). Przetworzenie wiadomości polega na wywołaniu odpowiadającej jej funkcji. Wywołanie funkcji powoduje stworzenie i umieszczenie na stosie nowej klatki początkowej. Przetwarzanie danej informacji zostaje zakończone, gdy nastąpi opróżnienie stosu.</p>

<p>Gdy stos jest pusty, środowisko rozpoczyna przetwarzanie kolejnej informacji z kolejki.</p>

<h2 id="Event_loop">Event loop</h2>

<p>Nazwa <code>Event loop</code> wiąże się ze sposobem implementacji, który zwykle przypomina następujący schemat:</p>

<pre class="brush: js">while(queue.waitForMessage()){
  queue.processNextMessage();
}</pre>

<p><code>queue.waitForMessage</code> oczekuje na wiadomość tak długo, dopóki jej nie otrzyma.</p>

<h3 id="Run-to-completion">"Run-to-completion"</h3>

<p>W tym modelu każda z wiadomości przetwarzana jest po całkowitym zakończeniu przetwarzania poprzedniej. Oferuje to pewne udogodnienia w analizie programu polegające na tym, że po uruchomieniu funkcji nie może zostać ona wyprzedzona oraz zostanie całkowicie wykonana przed uruchomieniem kolejnego kawałka kodu (dotyczy to również modyfikowanych przez funkcję danych). Model ten różni się od stosowanego np. w języku C, gdzie uruchomiona w danym wątku funkcja może w dowolnym punkcie zostać zatrzymana w celu uruchomienia innego fragmentu kodu w kolejnym wątku.</p>

<p><span id="result_box" lang="pl"><span>Wadą tego modelu jest to, że jeśli wykonanie wiadomości trwa zbyt długo, aplikacja internetowa nie jest w stanie przetworzyć interakcji użytkownika, takich jak kliknięcie lub scrollowanie.</span> <span>Przeglądarka łagodzi to za pomocą okna dialogowego "skrypt trwa zbyt długo, aby uruchomić".</span> <span>Dobrą praktyką jest dążenie, aby przetwarzanie komunikatów było krótkie, a jeśli to możliwe, należy rozbić wiadomości na kilka krótszych.</span></span></p>

<h3 id="Dodawanie_wiadomości_do_kolejki">Dodawanie wiadomości do kolejki</h3>

<p>W przeglądarkach internetowych wiadomości są dodawane przy każdym wystąpieniu eventu z dołączonym nasłuchiwaniem. Jeżeli brak nasłuchiwania event jest gubiony. Zatem przykładowo kliknięcie na element z nasłuchiwaniem doda nową wiadomość.</p>

<p>Funkcja <code><a href="/en-US/docs/Web/API/WindowTimers.setTimeout" title="/en-US/docs/window.setTimeout">setTimeout</a></code> przyjmuje dwa argumenty: wiadomość do dodania do kolejki oraz czas (argument opcjonalny, domyślnie 0). Podany czas reprezentuje minimalne opóźnienie, po którym wiadomość trafi do kolejki. Jeżeli w kolejce nie ma innej wiadomości, zostaje ona przetworzona natychmiast po upływie danego czasu opóźnienia. Jeżeli jednak w kolejce znajdują się wiadomości, wiadomość <code><a href="/en-US/docs/Web/API/WindowTimers.setTimeout" title="/en-US/docs/window.setTimeout">setTimeout</a></code> odczeka aż inne wiadomości zostaną przetworzone. Z tego powodu drugi argument tej funkcji określa czas minimalny,  nie gwarantowany. </p>

<p>Poniższy przykład ilustruje tę ideę (<code>setTimeout</code> nie zostanie uruchomiony bezpośrednio po upływie określonego czasu):</p>



<pre class="brush: js line-numbers  language-js"><code class="language-js"><span class="keyword token">const</span> s <span class="operator token">=</span> <span class="keyword token">new</span> <span class="class-name token">Date</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">.</span><span class="function token">getSeconds</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>

<span class="function token">setTimeout</span><span class="punctuation token">(</span><span class="keyword token">function</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="comment token">// zwraca "2", co oznacza, że callback nie zostaje wywołany bezpośrednio po upływie 500 millisekund.</span>
  console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="string token">"Uruchomiono po upływie "</span> <span class="operator token">+</span> <span class="punctuation token">(</span><span class="keyword token">new</span> <span class="class-name token">Date</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">.</span><span class="function token">getSeconds</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="operator token">-</span> s<span class="punctuation token">)</span> <span class="operator token">+</span> <span class="string token">" sekund"</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span><span class="punctuation token">,</span> <span class="number token">500</span><span class="punctuation token">)</span><span class="punctuation token">;</span>

<span class="keyword token">while</span><span class="punctuation token">(</span><span class="keyword token">true</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
  <span class="keyword token">if</span><span class="punctuation token">(</span><span class="keyword token">new</span> <span class="class-name token">Date</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">.</span><span class="function token">getSeconds</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="operator token">-</span> s <span class="operator token">&gt;=</span> <span class="number token">2</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
    console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="string token">"Ok, zapętlono na 2 sekundy"</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
    <span class="keyword token">break</span><span class="punctuation token">;</span>
  <span class="punctuation token">}</span>
<span class="punctuation token">}</span></code></pre>



<h3 id="Several_Runtime_communicating_together">Several Runtime communicating together</h3>

<p>A web worker or a cross-origin iframe has its own stack, heap, and message queue. Two distinct runtimes can only communicate through sending messages via the <a href="/en-US/docs/DOM/window.postMessage" title="/en-US/docs/DOM/window.postMessage"><code>postMessage</code></a> method. This method adds a message to the other runtime if the latter listens to <code>message</code> events.</p>

<h2 id="Never_blocking">Never blocking</h2>

<p>A very interesting property of the event loop model is that JavaScript, unlike a lot of other languages, never blocks. Handling I/O is typically performed via events and callbacks, so when the application is waiting for an <a href="/en-US/docs/Web/API/IndexedDB_API" title="/en-US/docs/IndexedDB">IndexedDB</a> query to return or an <a href="/en-US/docs/Web/API/XMLHttpRequest" title="/en-US/docs/DOM/XMLHttpRequest">XHR</a> request to return, it can still process other things like user input.</p>

<p>Legacy exceptions exist like <code>alert</code> or synchronous XHR, but it is considered as a good practice to avoid them. Beware, <a href="http://stackoverflow.com/questions/2734025/is-javascript-guaranteed-to-be-single-threaded/2734311#2734311" title="http://stackoverflow.com/questions/2734025/is-javascript-guaranteed-to-be-single-threaded/2734311#2734311">exceptions to the exception do exist</a> (but are usually implementation bugs rather than anything else).</p>