Асинхронность
JavaScript - синхронный, блокирующий и однопоточный язык.
Однопоточность — JavaScript работает в однопоточном режиме, т.е. только одна операция может быть выполнена в определенный момент времени.
Синхронное выполнение кода — код выполняется синхронно, т.е. следующая операция не выполняется до завершения предыдущей.
Синхронный и асинхронный код
Синхронный код - когда команды выполняются одна за другой. Пока выполняется одна операция, другие ожидают ее завершения.
Асинхронный код наоборот, начав выполнятся, может быть завершен в будущем, не блокируя поток выполнения команд.
Примеры асинронных событий:
События
HTTP-запрос
Таймеры (
setTimeout
/setInterval
)Чтение/запись файла (Node.js)
Обращение к базе (Node.js)
Все асинхронные функции, которые используются в JavaScript - это Web API, если речь идет о браузере, либо модули NodeJS, когда речь идет о бэкенде. Обращение к этим функциям запускает нативный код внутри Web API или Node.js (таймер, выполнение запроса, чтение и т. д.).
Есть два стиля написания асинхронного кода - это коллбэки и промисы.
Коллбэк - это функция, которая передается как аргумент при вызове функции, чтобы быть вызваны при возникновении какого-либо события (действие пользователя, завершение операции и т. д.). Эта функция может принимать какие-либо параметры, которые ей передает код, вызывающий коллбэк.
Если требуется выполнить несколько асинхронных операций подряд, может возникнуть ситуация, когда приходится делать несколько уровней вложенности коллбэков. Большую вложенность коллбэков называют “callback hell”.
Промисы, async/await
Плюсы промисов по сравнению с коллбэками:
Позволяют избежать большой вложенности (callback hell), используя цепочку
then
Ошибки в цепочке асинхронных событий можно обработать одним блоком
catch
в цепочки
Подробнее:
Событийный цикл (event loop)
После выполнения асинхронной функции, коллбэк, который был указан, добавляется очередь задач самим Web API или Node.js. Это может быть завершение таймера или какое-то событие:
действие пользователя (клик, ввод, скролл),
сообщение из воркера, iframe и т. д.
ответ от сервера
и т. д.
Дальше в дело вступает событийный цикл (event loop). Идея событийного цикла очень проста. Есть бесконечный цикл, в котором движок JavaScript ожидает задачи в очереди и добавляет их стек. Каждая задача в очереди - это какая-то функция (коллбэк)
Очередь представляет собой FIFO (fist in, first out), т. е. первая очередь в задаче будет выполнена раньше всех.
Пока выполняется одна задача, другие ждут завершения ее выполнения, т. е. освобождения стека. Если задача занимает слишком много времени, то веб-приложение не может обрабатывать действия пользователя в это время (например, скролл или клик).
Наглядная демонстрация event loop: http://latentflip.com/loupe/.
Задачи в очереди называют также макротасками или макрозадачи.
Между ними выполняются микрозадачи (микротаски). Они выполняются подряд, без прерываний, пока не будут выполнены все.
Откуда берутся микротаски?
Promise.then
Mutation observer
process.nextTick
(Node.js)
В процессе выполнения одной микротаски, можно создавать новые, которые будут выполнены в этом же промежутке, который называется microtask checkpoint.
Микрозадачи выполняются не только после выполнения макрозадач, но и после очистки стека.
Воркеры
Источники
Last updated