aboutsummaryrefslogtreecommitdiff
path: root/files/pl/learn/javascript/asynchronous/introducing/index.html
blob: d7fda6b10bdc1c05dcdf040bc0b71ce3bb3a596f (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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
---
title: Introducing asynchronous JavaScript
slug: Learn/JavaScript/Asynchronous/Introducing
translation_of: Learn/JavaScript/Asynchronous/Introducing
---
<div>{{LearnSidebar}}</div>

<div>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Concepts", "Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous")}}</div>

<p class="summary">W tym artykule po krótce omówimy problemy związane z synchronicznością JavaScriptu i zapoznamy się z innymi asynchronicznymi technikami, które napotkasz, pokazując jak mogą pomóc rozwiązać wymienione problemy.</p>

<table class="learn-box standard-table">
 <tbody>
  <tr>
   <th scope="row">Wymagania:</th>
   <td>Podstawy obsługi komputera, rzetelna znajomość podstaw JavaScriptu.</td>
  </tr>
  <tr>
   <th scope="row">Cel:</th>
   <td>Zapoznanie się z asynchronicznym JavaScriptem, czym się różni od synchronicznego JavaScriptu i w jakich przypadkach warto go użyć</td>
  </tr>
 </tbody>
</table>

<h2 id="Synchroniczny_JavaScript">Synchroniczny JavaScript</h2>

<p>By pomóc nam zrozumieć czym <strong><a href="/en-US/docs/Glossary/Asynchronous">asynchroniczny</a></strong><a href="/en-US/docs/Glossary/Asynchronous"> </a>JavaScript jest, musimy pierw zrozumieć czym jest <a href="/en-US/docs/Glossary/Synchronous">synchroniczny </a>JavaScript. Ten rozdział podsumuje informacje które widzieliśmy w porzednim artykule.</p>

<p>Wiele funkcji, które widzieliśmy w poprzednich modułach do nauki jest synchroniczna - uruchamiasz kod, a wynik jest zwracany tak szybko jak tylko przeglądarka może to zrobić. Spójrz na prosty przykład (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/basic-function.html">przykład użycia</a> i <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/basic-function.html">kod źródłowy</a>):</p>

<pre class="brush: js notranslate">const btn = document.querySelector('button');
btn.addEventListener('click', () =&gt; {
  alert('You clicked me!');

  let pElem = document.createElement('p');
  pElem.textContent = 'This is a newly-added paragraph.';
  document.body.appendChild(pElem);
});
</pre>

<p>W tym bloku każda linia jest wykonywana po kolei:</p>

<ol>
 <li>Pobieramy referencje do elementu {{htmlelement("button")}}, który jest już zdefiniowany w DOM'ie.</li>
 <li>Dodajemy obserwatora zdarzeń <code><a href="/en-US/docs/Web/API/Element/click_event">click</a></code> do niego, więc kiedy przycisk zostanie naciśnięty:
  <ol>
   <li>Wiadomość w funkcji <code><a href="/en-US/docs/Web/API/Window/alert">alert()</a></code> pojawi się w przeglądarce.</li>
   <li>Kiedy zamkniemy wiadomość, stworzymy element typu {{htmlelement("p")}}.</li>
   <li>Dodamuy do niego zawartość tekstową.</li>
   <li>Na końcu dodamy akapit do ciała dokumentu HTML.</li>
  </ol>
 </li>
</ol>

<p>Podczas gdy jest wykonywana każda z tych operacji, nic innego nie może się wydarzyć - renderowanie jest wstrzymane. Dzieje się to z powodu, który został opisany w <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">poprzednim rozdziale</a>, <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts#JavaScript_is_single_threaded">JavaScript jest jednowątkowy</a>. Na głównym wątku nie można wykonywać kilku rzeczy na raz. Wszelkie inne działania są zablokowane dopóki nie zakończy obecnej operacji.</p>

<p>W powyższym przykładzie, po kliknięciu przycisku akapit nie pojawi się dopóki nie wciśniesz przycisku OK w powiadomieniu. Możesz spróbować to samemu:</p>

<div class="hidden">
<pre class="brush: html notranslate">&lt;<span class="pl-ent">button</span>&gt;Click me&lt;/<span class="pl-ent">button</span>&gt;</pre>
</div>

<p>{{EmbedLiveSample('Synchronous_JavaScript', '100%', '70px')}}</p>

<div class="blockIndicator note">
<p><strong>Notatka</strong>: Ważnym jest by pamiętać, iż <code><a href="/en-US/docs/Web/API/Window/alert">alert()</a></code>,  choć jest bardzo użyteczny by zademonstrować blokowanie wątku przez synchroniczne operacje, toteż jego użycie w prawdziwych aplikacjach jest okropne i wskazuje na brak wyczucia smaku.</p>
</div>

<h2 id="Asynchroniczny_JavaScript">Asynchroniczny JavaScript</h2>

<p>Z powodów wymienionych wcześniej (np. blokowanie wątku), wiele funkcji Web API wykonuje kod asynchronicznie. Zwłaszcze te funkcje, które pobierają zasoby z zewnętrznego urządzenia, takie jak pliki z internetu, łączą się z bazą danych i zwracają z niej dane, łączą się ze strumieniem wideo z kamerki internetowej albo wyświetlają obraz w urządzeniach VR.</p>

<p>Dlaczego to jest problem pracować na kodzie synchronicznym? Spójrz na krótki przykład. Kiedy pobierasz obrazek z serwera, nie możesz od razu zwrócić wyniku. To znaczy, że poniższy pseudokod nie będzie działać: </p>

<pre class="brush: js notranslate">let response = fetch('myImage.png'); // fetch is asynchronous
let blob = response.blob();
// display your image blob in the UI somehow</pre>

<p>Jest to spowodowane tym, że nie wiesz ile dokładnie czasu zajmie Tobie pobieranie obrazka i kiedy spróbujesz wykonać drugą linię kodu to zostanie zwrócony błąd (może czasami, może za każdym razem) ponieważ odpowiedź (<code>response</code>) z serwera nie jest jeszcze gotowy. Zamiast tego Twój kod musi poczekać zanim odpowiedź zostanie zwrócona nim spróbuje na niej wykonać operacje.</p>

<p>Wyróżniamy dwa główne typy składni asynchronicznego kodu w JavaScript'cie. Starsza wywołanie zwrotne(<code>callback</code>) oraz nowsza obietnice (<code>promise</code>). W następnych rozdziałach kolejno przyjrzymy się każdej z nich.</p>

<h2 id="Async_callbacks">Async callbacks</h2>

<p>Async callbacks are functions that are specified as arguments when calling a function which will start executing code in the background. When the background code finishes running, it calls the callback function to let you know the work is done, or to let you know that something of interest has happened. Using callbacks is slightly old-fashioned now, but you'll still see them in use in a number of older-but-still-commonly-used APIs.</p>

<p>An example of an async callback is the second parameter of the {{domxref("EventTarget.addEventListener", "addEventListener()")}} method (as we saw in action above):</p>

<pre class="brush: js notranslate">btn.addEventListener('click', () =&gt; {
  alert('You clicked me!');

  let pElem = document.createElement('p');
  pElem.textContent = 'This is a newly-added paragraph.';
  document.body.appendChild(pElem);
});</pre>

<p>The first parameter is the type of event to be listened for, and the second parameter is a callback function that is invoked when the event is fired.</p>

<p>When we pass a callback function as an argument to another function, we are only passing the function's reference as an argument, i.e, the callback function is <strong>not </strong>executed immediately. It is “called back” (hence the name) asynchronously somewhere inside the containing function’s body. The containing function is responsible for executing the callback function when the time comes.</p>

<p>You can write your own function containing a callback easily enough. Let's look at another example that loads a resource via the <a href="/en-US/docs/Web/API/XMLHttpRequest"><code>XMLHttpRequest</code> API</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/xhr-async-callback.html">run it live</a>, and <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/xhr-async-callback.html">see the source</a>):</p>

<pre class="brush: js notranslate">function loadAsset(url, type, callback) {
  let xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.responseType = type;

  xhr.onload = function() {
    callback(xhr.response);
  };

  xhr.send();
}

function displayImage(blob) {
  let objectURL = URL.createObjectURL(blob);

  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
}

loadAsset('coffee.jpg', 'blob', displayImage);</pre>

<p>Here we create a <code>displayImage()</code> function that simply represents a blob passed to it as an object URL, then creates an image to display the URL in, appending it to the document's <code>&lt;body&gt;</code>. However, we then create a <code>loadAsset()</code> function that takes a callback as a parameter, along with a URL to fetch and a content type. It uses <code>XMLHttpRequest</code> (often abbreviated to "XHR") to fetch the resource at the given URL, then pass the response to the callback to do something with. In this case the callback is waiting on the XHR call to finish downloading the resource (using the <code><a href="/en-US/docs/Web/API/XMLHttpRequestEventTarget/onload">onload</a></code> event handler) before it passes it to the callback.</p>

<p>Callbacks are versatile — not only do they allow you to control the order in which functions are run and what data is passed between them, they also allow you to pass data to different functions depending on circumstance. So you could have different actions to run on the response downloaded, such as <code>processJSON()</code>, <code>displayText()</code>, etc.</p>

<p>Note that not all callbacks are async — some run synchronously. An example is when we use {{jsxref("Array.prototype.forEach()")}} to loop through the items in an array (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/foreach.html">see it live</a>, and <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/foreach.html">the source</a>):</p>

<pre class="brush: js notranslate">const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus'];

gods.forEach(function (eachName, index){
  console.log(index + '. ' + eachName);
});</pre>

<p>In this example we loop through an array of Greek gods and print the index numbers and values to the console. The expected parameter of <code>forEach()</code> is a callback function, which itself takes two parameters, a reference to the array name and index values. However, it doesn't wait for anything — it runs immediately.</p>

<h2 id="Promises">Promises</h2>

<p>Promises are the new style of async code that you'll see used in modern Web APIs. A good example is the <code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch">fetch()</a></code> API, which is basically like a modern, more efficient version of {{domxref("XMLHttpRequest")}}. Let's look at a quick example, from our <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">Fetching data from the server</a> article:</p>

<pre class="brush: js notranslate">fetch('products.json').then(function(response) {
  return response.json();
}).then(function(json) {
  products = json;
  initialize();
}).catch(function(err) {
  console.log('Fetch problem: ' + err.message);
});</pre>

<div class="blockIndicator note">
<p><strong>Note</strong>: You can find the finished version on GitHub (<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-script.js">see the source here</a>, and also <a href="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/">see it running live</a>).</p>
</div>

<p>Here we see <code>fetch</code><code>()</code> taking a single parameter — the URL of a resource you want to fetch from the network — and returning a <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">promise</a>. The promise is an object representing the completion or failure of the async operation. It represents an intermediate state, as it were. In essence, it's the browser's way of saying "I promise to get back to you with the answer as soon as I can," hence the name "promise."</p>

<p>This concept can take practice to get used to; it feels a little like {{interwiki("wikipedia", "Schrödinger's cat")}} in action. Neither of the possible outcomes have happened yet, so the fetch operation is currently waiting on the result of the browser trying to complete the operation at some point in the future. We've then got three further code blocks chained onto the end of the <code>fetch()</code>:</p>

<ul>
 <li>Two <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then">then()</a></code> blocks. Both contain a callback function that will run if the previous operation is successful, and each callback receives as input the result of the previous successful operation, so you can go forward and do something else to it. Each <code>.then()</code> block returns another promise, meaning that you can chain multiple <code>.then()</code> blocks onto each other, so multiple asynchronous operations can be made to run in order, one after another.</li>
 <li>The <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch">catch()</a></code> block at the end runs if any of the <code>.then()</code> blocks fail — in a similar way to synchronous <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/try...catch">try...catch</a></code> blocks, an error object is made available inside the <code>catch()</code>, which can be used to report the kind of error that has occurred. Note however that synchronous <code>try...catch</code> won't work with promises, although it will work with <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">async/await</a>, as you'll learn later on.</li>
</ul>

<div class="blockIndicator note">
<p><strong>Note</strong>: You'll learn a lot more about promises later on in the module, so don't worry if you don't understand them fully yet.</p>
</div>

<h3 id="The_event_queue">The event queue</h3>

<p>Async operations like promises are put into an <strong>event queue</strong>, which runs after the main thread has finished processing so that they <em>do not block</em> subsequent JavaScript code from running. The queued operations will complete as soon as possible then return their results to the JavaScript environment.</p>

<h3 id="Promises_versus_callbacks">Promises versus callbacks</h3>

<p>Promises have some similarities to old-style callbacks. They are essentially a returned object to which you attach callback functions, rather than having to pass callbacks into a function.</p>

<p>However, promises are specifically made for handling async operations, and have many advantages over old-style callbacks:</p>

<ul>
 <li>You can chain multiple async operations together using multiple <code>.then()</code> operations, passing the result of one into the next one as an input. This is much harder to do with callbacks, which often ends up with a messy "pyramid of doom" (also known as <a href="http://callbackhell.com/">callback hell</a>).</li>
 <li>Promise callbacks are always called in the strict order they are placed in the event queue.</li>
 <li>Error handling is much better — all errors are handled by a single <code>.catch()</code> block at the end of the block, rather than being individually handled in each level of the "pyramid".</li>
 <li>Promises avoid inversion of control, unlike old-style callbacks, which lose full control of how the function will be executed when passing a callback to a third-party library.</li>
</ul>

<h2 id="The_nature_of_asynchronous_code">The nature of asynchronous code</h2>

<p>Let's explore an example that further illustrates the nature of async code, showing what can happen when we are not fully aware of code execution order and the problems of trying to treat asynchronous code like synchronous code. The following example is fairly similar to what we've seen before (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/async-sync.html">see it live</a>, and <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/async-sync.html">the source</a>). One difference is that we've included a number of {{domxref("console.log()")}} statements to illustrate an order that you might think the code would execute in.</p>

<pre class="brush: js notranslate">console.log ('Starting');
let image;

fetch('coffee.jpg').then((response) =&gt; {
  console.log('It worked :)')
  return response.blob();
}).then((myBlob) =&gt; {
  let objectURL = URL.createObjectURL(myBlob);
  image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
}).catch((error) =&gt; {
  console.log('There has been a problem with your fetch operation: ' + error.message);
});

console.log ('All done!');</pre>

<p>The browser will begin executing the code, see the first <code>console.log()</code> statement (<code>Starting</code>) and execute it, and then create the <code>image</code> variable.</p>

<p>It will then move to the next line and begin executing the <code>fetch()</code> block but, because <code>fetch()</code> executes asynchronously without blocking, code execution continues after the promise-related code, thereby reaching the final <code>console.log()</code> statement (<code>All done!</code>) and outputting it to the console.</p>

<p>Only once the <code>fetch()</code> block has completely finished running and delivering its result through the <code>.then()</code> blocks will we finally see the second <code>console.log()</code> message (<code>It worked :)</code>) appear. So the messages have appeared in a different order to what you might expect:</p>

<ul>
 <li>Starting</li>
 <li>All done!</li>
 <li>It worked :)</li>
</ul>

<p>If this confuses you, then consider the following smaller example:</p>

<pre class="brush: js notranslate">console.log("registering click handler");

button.addEventListener('click', () =&gt; {
  console.log("get click");
});

console.log("all done");</pre>

<p>This is very similar in behavior — the first and third <code>console.log()</code> messages will be shown immediately, but the second one is blocked from running until someone clicks the mouse button. The previous example works in the same way, except that in that case the second message is blocked on the promise chain fetching a resource then displaying it on screen, rather than a click.</p>

<p>In a less trivial code example, this kind of setup could cause a problem — you can't include an async code block that returns a result, which you then rely on later in a sync code block. You just can't guarantee that the async function will return before the browser has processed the sync block.</p>

<p>To see this in action, try taking a local copy of <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/async-sync.html">our example</a>, and changing the fourth <code>console.log()</code> call to the following:</p>

<pre class="brush: js notranslate">console.log ('All done! ' + image.src + 'displayed.');</pre>

<p>You should now get an error in your console instead of the third message:</p>

<pre class="notranslate"><span class="message-body-wrapper"><span class="message-flex-body"><span class="devtools-monospace message-body">TypeError: image is undefined; can't access its "src" property</span></span></span></pre>

<p>This is because at the time the browser tries to run the third <code>console.log()</code> statement, the <code>fetch()</code> block has not finished running so the <code>image</code> variable has not been given a value.</p>

<div class="blockIndicator note">
<p><strong>Note</strong>: For security reasons, you can't <code>fetch()</code> files from your local filesystem (or run other such operations locally); to run the above example locally you'll have to run the example through a <a href="/en-US/docs/Learn/Common_questions/set_up_a_local_testing_server">local webserver</a>.</p>
</div>

<h2 id="Active_learning_make_it_all_async!">Active learning: make it all async!</h2>

<p>To fix the problematic <code>fetch()</code> example and make the three <code>console.log()</code> statements appear in the desired order, you could make the third <code>console.log()</code> statement run async as well. This can be done by moving it inside another <code>.then()</code> block chained onto the end of the second one, or by simply moving it inside the second <code>then()</code> block. Try fixing this now.</p>

<div class="blockIndicator note">
<p><strong>Note</strong>: If you get stuck, you can <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/async-sync-fixed.html">find an answer here</a> (see it <a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/async-sync-fixed.html">running live</a> also). You can also find a lot more information on promises in our <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">Graceful asynchronous programming with Promises</a> guide, later on in the module.</p>
</div>

<h2 id="Conclusion">Conclusion</h2>

<p>In its most basic form, JavaScript is a synchronous, blocking, single-threaded language, in which only one operation can be in progress at a time. But web browsers define functions and APIs that allow us to register functions that should not be executed synchronously, and should instead be invoked asynchronously when some kind of event occurs (the passage of time, the user's interaction with the mouse, or the arrival of data over the network, for example). This means that you can let your code do several things at the same time without stopping or blocking your main thread.</p>

<p>Whether we want to run code synchronously or asynchronously will depend on what we're trying to do.</p>

<p>There are times when we want things to load and happen right away. For example when applying some user-defined styles to a webpage you'll want the styles to be applied as soon as possible.</p>

<p>If we're running an operation that takes time however, like querying a database and using the results to populate templates, it is better to push this off the main thread and complete the task asynchronously. Over time, you'll learn when it makes more sense to choose an asynchronous technique over a synchronous one.</p>

<ul>
</ul>

<p>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Concepts", "Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous")}}</p>

<h2 id="In_this_module">In this module</h2>

<ul>
 <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts">General asynchronous programming concepts</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">Introducing asynchronous JavaScript</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Cooperative asynchronous JavaScript: Timeouts and intervals</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">Graceful asynchronous programming with Promises</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">Making asynchronous programming easier with async and await</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">Choosing the right approach</a></li>
</ul>