aboutsummaryrefslogtreecommitdiff
path: root/files/uk/web/javascript/eventloop/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'files/uk/web/javascript/eventloop/index.html')
-rw-r--r--files/uk/web/javascript/eventloop/index.html152
1 files changed, 152 insertions, 0 deletions
diff --git a/files/uk/web/javascript/eventloop/index.html b/files/uk/web/javascript/eventloop/index.html
new file mode 100644
index 0000000000..ab47fd4b48
--- /dev/null
+++ b/files/uk/web/javascript/eventloop/index.html
@@ -0,0 +1,152 @@
+---
+title: Модель конкурентності та цикл подій
+slug: Web/JavaScript/EventLoop
+tags:
+ - JavaScript
+ - обробники подій
+ - події
+ - цикл подій
+ - черга
+translation_of: Web/JavaScript/EventLoop
+---
+<div>{{JsSidebar("Advanced")}}</div>
+
+<p>JavaScript має модель одночасності, що базується на <strong>циклі подій</strong>, який є відповідальним за виконання коду, збір та обробку подій та виконання підзадач з черги. Ця модель доволі сильно відрізняється від моделей у інших мовах, таких як С та Java.</p>
+
+<h2 id="Концепції_виконання">Концепції виконання</h2>
+
+<p>Наступні розділи пояснють теоретичну модель. Сучасні імплементації JavaScript реалізують і значно оптимізують описану семантику.</p>
+
+<h3 id="Візуальне_відображення">Візуальне відображення</h3>
+
+<p style="text-align: center;"><img alt="Стек, купа, черга" src="https://mdn.mozillademos.org/files/17036/visual.png" style="height: 270px; width: 294px;"></p>
+
+<h3 id="Стек">Стек</h3>
+
+<p>Виклики функцій утворюють стек <em>фреймів (frames)</em>.</p>
+
+<pre class="brush: js">function foo(b) {
+  var a = 10;
+  return a + b + 11;
+}
+
+function bar(x) {
+ var y = 3;
+ return foo(x * y);
+}
+
+console.log(bar(7)); // вертає 42
+</pre>
+
+<p>Коли викликається <code><font face="Consolas, Liberation Mono, Courier, monospace">bar</font></code>, утворюється перший фрейм, який містить аргументи та локальні змінні функції <code>bar</code>. Коли <code><font face="Consolas, Liberation Mono, Courier, monospace">bar</font></code> викликає <code>foo</code>, створюється другий фрейм та розміщується над першим, він містить аргументи та локальні змінні функції <code>foo</code>. Коли <code>foo</code> повертає значення, верхній фрейм виштовхується зі стеку (залишаючи лише фрейм виклику <font face="Consolas, Liberation Mono, Courier, monospace"><code>bar</code></font>). Коли <code><font face="Consolas, Liberation Mono, Courier, monospace">bar</font></code> повертає значення, стек стає порожнім.</p>
+
+<h3 id="Купа">Купа</h3>
+
+<p>Об'єкти розподіляються у купі, яка є лише назвою для позначення великої (здебільшого не структурованої) області пам'яті.</p>
+
+<h3 id="Черга">Черга</h3>
+
+<p>Процес виконання JavaScript використовує чергу повідомлень, яка є списком повідомлень, що мають бути опрацьовані. Кожне повідомлення має пов'язану функцію, яка викликається для обробки цього повідомлення </p>
+
+<p>В певний момент {{anch("Цикл_подій", "циклу подій")}} процес виконання починає обробку повідомлень з черги, починаючи з найстаршого. Для цього повідомлення видаляється з черги, а його пов'язана функція викликається з повідомленням в якості вхідного параметра. Як завжди, виклик функції створює новий фрейм стеку для цієї функції.</p>
+
+<p>Опрацювання функцій продовжується, доки стек знову не стане порожнім. Тоді цикл подій опрацює наступне повідомлення у черзі (якщо воно є).</p>
+
+<h2 id="Цикл_подій">Цикл подій</h2>
+
+<p><strong>Цикл подій</strong> отримав свою назву через те, як він зазвичай реалізується. Як правило, це схоже на:</p>
+
+<pre class="brush: js">while (queue.waitForMessage()) {
+ queue.processNextMessage();
+}</pre>
+
+<p><code>queue.waitForMessage</code> синхронно чекає на прибуття повідомлення (якщо воно вже не надійшло і не чекає на обробку).</p>
+
+<h3 id="Виконання_до_завершення">"Виконання до завершення"</h3>
+
+<p>Кожне повідомлення обробляється до завершення, перш, ніж обробляти будь-яке інше повідомлення.</p>
+
+<p>Це надає деякі приємні властивості для вашої програми, в тому числі той факт, що, коли виконується функція, вона не може бути попередньо вилучена і виконається до кінця перш, ніж буде запущено будь-який інший код (і зможе змінювати дані, якими користується функція). Це відрізняється, наприклад, від C, де, якщо функція виконується у потоці, її можна зупинити в будь-якій точці, щоб запустити інший код в іншому потоці.</p>
+
+<p>Зворотним боком цієї моделі є те, що, якщо обробка повідомлення займає надто багато часу, веб-застосунок не може обробити взаємодії користувача, як-от натискання чи прокручування. Веб-переглядач пом'якшує це діалоговим вікном "a script is taking too long to run" (виконання сценарію займає забагато часу). Гарною практикою є робити обробку повідомлень короткою і, за можливості, розбивати одне повідомлення на декілька.</p>
+
+<h3 id="Додавання_повідомлень">Додавання повідомлень</h3>
+
+<p>У веб-переглядачах повідомлення додаються щоразу, коли виникає подія, до якої приєднаний прослуховувач подій. Якщо прослуховувача немає, подія втрачається. Отже, натискання на елементі з обробником подій натискання додасть повідомлення, так само з будь-якою іншою подією.</p>
+
+<p>Функція <code><a href="/uk/docs/Web/API/WindowTimers/setTimeout">setTimeout</a></code> викликається з двома аргументами: повідомлення, що додається до черги, та значення часу (необов'язкове; за замовчуванням <code>0</code>). <em>Значення часу </em>відображає (мінімальну) затримку, після якої повідомлення буде, власне, додане до черги. Якщо в черзі немає інших повідомлень, це повідомлення буде оброблене одразу після затримки. Однак, якщо там є повідомлення, повідомленню <code>setTimeout</code> доведеться зачекати, доки не будуть оброблені інші повідомлення. З цієї причини другий аргумент вказує <em>мінімальний</em>, а не гарантований час.</p>
+
+<p>Ось приклад, який демонструє цю концепцію (<code>setTimeout</code> не виконується негайно після того, як його таймер завершився): </p>
+
+<pre class="brush: js">const s = new Date().getSeconds();
+
+setTimeout(function() {
+ // виводить "2", тобто, функція зворотного виклику не запустилась одразу через 500 мілісекунд.
+ console.log("Функція запустилась через " + (new Date().getSeconds() - s) + " секунд");
+}, 500)
+
+while (true) {
+ if (new Date().getSeconds() - s &gt;= 2) {
+ console.log("Добре, виконувалось 2 секунди")
+ break;
+ }
+}</pre>
+
+<h3 id="Нульові_затримки">Нульові затримки</h3>
+
+<p>Нульова затримка насправді не означає, що зворотній виклик запуститься через нуль мілісекунд. Виклик {{domxref ("WindowTimers.setTimeout", "setTimeout")}} із затримкою в <code>0</code> (нуль) мілісекунд не виконує функцію зворотного виклику після заданого інтервалу.</p>
+
+<p>Виконання залежить від кількості задач, що чекають у черзі. У наведеному нижче прикладі повідомлення <code>"це просто повідомлення"</code> буде написане у консоль раніше, ніж буде оброблене повідомлення у зворотному виклику, оскільки затримка - це <em>мінімальний</em> час, необхідний для обробки запиту (а не <em>гарантований</em> час).</p>
+
+<p>Загалом, <code>setTimeout</code> має чекати, доки виконається весь код для повідомлень у черзі, незважаючи на те, що ви вказали певний часовий ліміт для своєї функції <code>setTimeout</code>.</p>
+
+<pre class="brush: js">(function() {
+
+ console.log('це початок');
+
+ setTimeout(function cb() {
+ console.log('Зворотний виклик 1: це повідомлення зворотного виклику');
+ });
+
+ console.log('це просто повідомлення');
+
+ setTimeout(function cb1() {
+ console.log('Зворотний виклик 2: це повідомлення зворотного виклику');
+ }, 0);
+
+ console.log('це кінець');
+
+})();
+
+// "це початок"
+// "це просто повідомлення"
+// "це кінець"
+// "Зворотний виклик 1: це повідомлення зворотного виклику"
+// "Зворотний виклик 2: це повідомлення зворотного виклику"
+</pre>
+
+<h3 id="Декілька_процесів_виконання_що_спілкуються_між_собою">Декілька процесів виконання, що спілкуються між собою</h3>
+
+<p>Веб-виконавець або iframe перехресного походження має свій стек, купу та чергу повідомлень. Два різних процеси виконання можуть спілкуватися надсиланням повідомлень за допомогою методу <a href="/uk/docs/Web/API/Window/postMessage"><code>postMessage</code></a>. Цей метод додає повідомлення до іншого процесу виконання, якщо останній прослуховує події <code>message</code>.</p>
+
+<h2 id="Жодного_блокування">Жодного блокування</h2>
+
+<p>Дуже цікавою властивістю моделі циклу подій є те, що JavaScript, на відміну від багатьох інших мов, ніколи не блокує. Управління введенням/виводом зазвичай здійснюється за допомогою подій та зворотних викликів, тому, коли програма чекає на результат запиту <a href="/uk/docs/Web/API/IndexedDB_API">IndexedDB</a> чи запиту <a href="/uk/docs/Web/API/XMLHttpRequest">XHR</a>, вона може опрацьовувати інші події, такі як введення даних користувачем.</p>
+
+<p>Існують спадкові винятки, такі як <code>alert</code> або синхронний XHR, але вважається гарною практикою їх уникати. Будьте обережні: <a href="http://stackoverflow.com/questions/2734025/is-javascript-guaranteed-to-be-single-threaded/2734311#2734311">існують винятки з винятку</a> (але зазвичай це помилки реалізації, а не що-небудь інше).</p>
+
+<h2 id="Специфікації">Специфікації</h2>
+
+<table class="standard-table">
+ <tbody>
+ <tr>
+ <th scope="col">Специфікація</th>
+ </tr>
+ <tr>
+ <td>{{SpecName('HTML WHATWG', 'webappapis.html#event-loops', 'Event loops')}}</td>
+ </tr>
+ <tr>
+ <td><a href="https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#what-is-the-event-loop">Node.js Event Loop</a></td>
+ </tr>
+ </tbody>
+</table>