Что такое event loop

Событийный цикл: микрозадачи и макрозадачи

Поток выполнения в браузере, равно как и в Node.js, основан на событийном цикле.

Понимание работы событийного цикла важно для оптимизаций, иногда для правильной архитектуры.

В этой главе мы сначала разберём теорию, а затем рассмотрим её практическое применение.

Событийный цикл

Идея событийного цикла очень проста. Есть бесконечный цикл, в котором движок JavaScript ожидает задачи, исполняет их и снова ожидает появления новых.

Общий алгоритм движка:

Это формализация того, что мы наблюдаем, просматривая веб-страницу. Движок JavaScript большую часть времени ничего не делает и работает, только если требуется исполнить скрипт/обработчик или обработать событие.

…Но, возможно, мы хотим что-нибудь показать во время выполнения задачи, например, индикатор выполнения.

Так будет красивее:

Пример 3: делаем что-нибудь после события

В обработчике события мы можем решить отложить некоторые действия, пока событие не «всплывёт» и не будет обработано на всех уровнях. Мы можем добиться этого, обернув код в setTimeout с нулевой задержкой.

Макрозадачи и Микрозадачи

Помимо макрозадач, описанных в этой части, существуют микрозадачи, упомянутые в главе Микрозадачи.

Сразу после каждой макрозадачи движок исполняет все задачи из очереди микрозадач перед тем, как выполнить следующую макрозадачу или отобразить изменения на странице, или сделать что-то ещё.

Какой здесь будет порядок?

Более подробное изображение событийного цикла выглядит так:

Все микрозадачи завершаются до обработки каких-либо событий или рендеринга, или перехода к другой макрозадаче.

Это важно, так как гарантирует, что общее окружение остаётся одним и тем же между микрозадачами – не изменены координаты мыши, не получены новые данные по сети и т.п.

Итого

Более подробный алгоритм событийного цикла (хоть и упрощённый в сравнении со спецификацией):

Чтобы добавить в очередь новую макрозадачу:

Этот способ можно использовать для разбиения больших вычислительных задач на части, чтобы браузер мог реагировать на пользовательские события и показывать прогресс выполнения этих частей.

Также это используется в обработчиках событий для отложенного выполнения действия после того, как событие полностью обработано (всплытие завершено).

Для добавления в очередь новой микрозадачи:

События пользовательского интерфейса и сетевые события в промежутках между микрозадачами не обрабатываются: микрозадачи исполняются непрерывно одна за другой.

Поэтому queueMicrotask можно использовать для асинхронного выполнения функции в том же состоянии окружения.

Для длительных тяжёлых вычислений, которые не должны блокировать событийный цикл, мы можем использовать Web Workers.

Это способ исполнить код в другом, параллельном потоке.

Web Workers могут обмениваться сообщениями с основным процессом, но они имеют свои переменные и свой событийный цикл.

Web Workers не имеют доступа к DOM, поэтому основное их применение – вычисления. Они позволяют задействовать несколько ядер процессора одновременно.

Задачи

Что код выведет в консоли?

Давайте разберем что здесь происходит.

Изначально в стеке выполнения находится сам скрипт, поэтому сначала выполняется только он.

Источник

Многопоточность на Node.js. Event Loop

Инфа будет полезна JS-разработчикам, которые хотят глубоко понимать суть работы с Node.js и Event Loop. Вы сможете осознанно и более гибко управлять потоком выполнения программы (web-сервера).

Эту статью я составил по материалам своего недавнего доклада для коллег.
В конце статьи есть полезные материалы для самостоятельного изучения.

Как устроена Node.js. Возможности асинхрона

Давайте посмотрим на этот код: он отлично демонстрирует синхронность выполнения кода в Node.js. Делается запрос на GitHub, затем записываем данные в файл и выводим результат в консоли. Что понятно из этого синхронного кода?

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Предположим, что это абстрактный web-сервер, который по какому-то роутеру делает операции. Если по данному роутеру приходит входящий запрос, мы делаем дальше запрос, читаем файл, выводим в консоль. Соответственно, то время, которое тратится на запрос и чтение файла, сервер будет заблокирован, никакие другие входящие запросы он обрабатывать не сможет, выполнять другие операции — тоже.

Какие есть варианты решения данной проблемы?

Для первого варианта (многопоточность) есть хороший пример с web-сервером Apache vs Nginx.

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Раньше Apache под каждый входящий запрос поднимал поток: сколько было запросов, столько же было и потоков. В это время Nginx имел преимущество, т. к. использовал неблокирующий ввод/вывод. Здесь вы можете видеть, что с увеличением количества входящих запросов увеличивается объём потребляемой памяти именно у Apache, а на следующем слайде количество обрабатываемых запросов в секунду с количеством подключений у Nginx выше.

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Наглядно показано, что неблокирующий ввод/вывод эффективнее.

Неблокирующий ввод/вывод стал возможным благодаря современным операционным системам, которые предоставляют данный механизм — демультиплексор событий.

Демультиплексор — это механизм, который принимает от приложения запрос, регистрирует его и выполняет.

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

В верхней части схемы видно, что у нас есть приложение и в нём выполняются операции (пусть это будет чтение файла). Для этого делается запрос в демультиплексор событий, сюда отправляется ресурс (ссылка на файл), нужная операция и callback. Демультиплексор событий регистрирует этот запрос и возвращает управление непосредственно приложению — таким образом, оно не блокируется. Затем он выполняет операции над файлом, и после этого, когда файл будет прочитан, callback регистрируется в очереди на выполнение. Затем Event Loop постепенно синхронно обрабатывает каждый callback из этой очереди. И, соответственно, возвращает результат приложению. Дальше (если необходимо) всё делается снова.

Таким образом, благодаря данному неблокирующему вводу/выводу Node.js может быть асинхронным.

Уточню, что в данном случае неблокирующий ввод/вывод предоставляет нам именно операционная система. К неблокирующему вводу/выводу (вообще в принципе к операциям ввода/вывода) мы относим сетевые запросы и работу с файлами.

Это общая концепция неблокирующего ввода/вывода. Когда такая возможность появилась, Райан Дал (Ryan Dahl) — разработчик Node.js — был вдохновлён опытом Nginx, которая использовала неблокирующий ввод/вывод, и решил создать платформу именно для разработчиков. Первое, что ему нужно было сделать, — «подружить» свою платформу с демультиплексором событий. Проблема была в том, что в каждой операционной системе демультиплексор реализован по-разному, и ему пришлось написать обёртку, которая впоследствии стала называться libuv. Это библиотека, написанная на C. Она предоставляет единый интерфейс работы с этими демультиплексорами событий.

Особенности libuv-библиотеки

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

В Linux, в принципе, на текущий момент все операции с локальными файлами — блокирующие. Т. е. вроде бы как есть неблокирующий ввод/вывод, но именно при работе с локальными файлами операция всё равно блокирующая. Именно поэтому для эмуляции неблокирующего ввода/вывода libuv использует внутри потоки. Из коробки поднимается 4 потока, и здесь нужно сделать самый важный вывод: если мы выполняем какие-то 4 тяжёлые операции над локальными файлами, соответственно, мы заблокируем всё наше приложение (именно в ОС Linux, в других ОС такого нет).

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

На этом слайде мы видим архитектуру Node.js. Для взаимодействия с операционной системой используется библиотека libuv, написанная на C; для компиляции кода JavaScript’a в машинный код используется движок Google V8, также есть Node.js Core library, где собраны модули для работы с сетевыми запросами, файловой системой и модуль для логирования. Чтобы всё это друг с другом взаимодействовало, написаны Node.js Bindings. Эти 4 компонента составляют саму структуру Node.js. Сам механизм Event Loop’a находится в libuv.

Event Loop

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Это простейшее представление, как выглядит Event Loop. Есть определённая очередь событий, есть бесконечный цикл событий, который синхронно выполняет операции из очереди, и он распределяет их дальше.

На этом слайде показано, как выглядит Event Loop непосредственно в самой Node.js.
Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Там реализация поинтереснее и посложнее. По сути, Event Loop — это цикл событий, и он бесконечен до тех пор, пока есть что выполнять. Event Loop в Node.js делится на несколько фаз. (Фазы со слайда 8 надо сопоставлять с исходным кодом на слайде 9.)

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

1 фаза — таймеры

Данная фаза выполняется непосредственно Event Loop’ом. (Фрагмент кода с uv_update_time) — здесь просто обновляется время, когда начал работать Event Loop.

uv_run_timers — в этом методе выполняется следующее действие с таймером. Есть определённый стек, точнее, куча таймеров, это, по сути, то же самое, что очередь, где находятся таймеры. Берётся таймер с самым маленьким временем, сравнивается с текущим времени Event Loop’а, и, если настало время для исполнения данного таймера, выполняется его callback. Здесь стоит отметить что в Node.js есть реализация setTimeout и есть setInterval. Для libuv это, по сути, одно и то же, только в setInterval ещё есть флаг repeat.

Соответственно, если у данного таймера стоит флаг repeat, то он снова помещается в очередь событий и потом точно так же обрабатывается.

2 фаза — I/O-callback’и

Здесь надо вернуться к диаграмме про неблокирующий ввод/вывод.

Когда демультиплексор событий выполняет чтение какого-либо файла и ставит выполнение callback’а в очередь, это как раз соответствует этапу I/O-callback. Здесь выполняются callback’и для неблокирующего ввода/вывода, т. е. это именно те функции, которые используются после запроса в базу данных или другой ресурс или на чтение/запись файла. Они выполняются именно на данной фазе.

На слайде 9 выполнение функции I/O-callback’и запускает строка 367: ran_pending = uv_run_pending(loop).

3 фаза — ожидание, подготовка

Это внутренние операции для callback’ов, по сути, мы не можем влиять на фазу, только косвенно. Есть process.nextTick, его callback может ненамеренно быть исполнен на фазе «ожидание, подготовка». process.nextTick выполняется на текущей фазе, т. е., по сути, process.nextTick может сработать абсолютно на любой фазе. Какого-то готового инструмента, чтобы запустить код на фазе «ожидание, подготовка», в Node.js нет.

На слайде 9 этой фазе соответствуют строки 368, 369:
uv_run_idle(loop) — ожидание;
uv_run_prepare(loop) — подготовка.

4 фаза — опрос

Здесь выполняется весь наш код, который мы пишем на JS. Первоначально все запросы, которые мы делаем, попадают именно сюда, и именно здесь Node.js может быть заблокирована. Если сюда попадёт какая-либо тяжёлая операция по вычислению, то на этом этапе наше приложение может просто зависнуть и ожидать, пока не выполнится данная операция.
На слайде 9 функция опроса содержится в строке 370: uv_io_poll(loop, timeout).

5 фаза — проверка

В Node.js есть таймер setImmediate, его callback’и выполняются на этой фазе.
В исходном коде это строка 371: uv_run_check(loop).

6 фаза (последняя) — callback’и событий close

Например, web-сокету нужно закрыть соединение, на этой фазе будет вызван callback этого события.

В исходном коде это строка 372: uv_run_closing_handless(loop).

И в итоге Event Loop Node.js выглядит следующим образом

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Сначала в очереди таймеров выполняется тот таймер, срок которого подошёл.

Дальше выполняются I/O-callback’и.

Затем — основой код, дальше — setImmediate и события close.

После этого всё повторяется по кругу. Чтобы продемонстрировать это, открою код. Как он будет выполняться?

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

У нас нет таймеров в очереди, поэтому Event Loop двигается дальше. I/O-callback’ов тоже нет, поэтому идём сразу на фазу опроса. Весь код, который здесь есть, изначально выполняется на фазе опроса. Поэтому сначала печатаем script_start, setInterval у нас помещается в очередь таймеров (не выполняется, просто помещается). setTimeout также помещается в очередь таймеров, и затем выполняются промисы: сначала promise 1 и затем promise 2.

В следующий тик (цикл событий) мы возвращаемся на этап таймеров, здесь в очереди уже есть 2 таймера: setInterval и setTimeout. Они оба с задержкой 0, соответственно, они готовы к выполнению.

Выполняются (выводятся в консоль) setInterval, затем setTimeout 1. Callback’ов неблокирующего ввода/вывода нет, дальше будет фаза опроса, в консоль выводятся promise 3 и promise 4.

Дальше регистрируется таймер setTimeout. На этом тик заканчивается, идём в следующий тик. Там — снова таймеры, выполняется вывод в консоль setInterval и setTimeout 2, затем выводятся promise 5 и promise 6.

Event Loop мы рассмотрели и теперь можем более подробно поговорить о многопоточности.

Многопоточность — модуль worker_threads

Ещё в Node.js есть модуль cluster, но он не поднимает потоки — он поднимает ещё несколько процессов. Масштабируемость приложения — его основная цель.

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Как вообще выглядит 1 процесс:
1 процесс Node.js, 1 поток, 1 Event Loop, 1 движок V8 и libuv.

Если мы запускаем X потоков, то у нас это выглядит так:
1 процесс Node.js, X потоков, Х Event Loop’ов, X движков V8 и X libuv.

Схематично это выглядит следующим образом

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Давайте разберём пример.

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Простейший web-сервер на Express’е. Есть 2 route’а — / и /fat-operation.

Также есть функция generateRandomArr(). Она наполняет массив двумя миллионами записей и сортирует его. Запустим сервер.

Делаем запрос на /fat-operation. И в тот момент, когда выполняется операция сортировки массива, отправляем ещё один запрос на route /, но для получения ответа нам приходится ждать до тех пор, пока не выполнится сортировка массива. Это классическая реализация через один поток. Теперь подключаем модуль worker_threads.

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Делаем запрос на /fat-operation и следом — на /, от которого тут же получаем ответ — Hello world!

Для операции сортировки массива мы подняли отдельный поток, у которого есть свой экземпляр Event Loop, и он никак не влияет на выполнение кода в основном потоке.

Поток будет «уничтожен», когда у него не будет операций для выполнения.

Смотрим исходный код. Регистрируем worker в строке 26 и, если нужно, передаём ему данные. В данном случае я ничего не передаю. И затем подписываемся на события: на ошибку и на месседж. В самом worker’е происходит вызов функции, массив из двух миллионов записей сортируется. Как только он отсортировался, мы через post_message отправляем результат в основной поток ok.

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

В основном потоке мы ловим это сообщение и отправляем результат finish. У worker’а и основного потока общая память, поэтому мы имеем доступ к глобальным переменным всего процесса. Когда мы передаём данные из основного потока в worker, worker получает только копию.

Основной поток и поток worker’а мы можем описать в одном файле. Модуль worker_threads предоставляет API, благодаря которому мы можем определить, в каком потоке сейчас выполняется код.

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Дополнительная информация

Делюсь ссылками на полезные ресурсы и ссылкой на презентацию Райана Дала, когда он презентовал Event Loop (интересно посмотреть).

Источник

Что ты такое, Event Loop? Или как устроен цикл событий в браузере Chrome

Как думаете, что произойдет, если запустить в консоли браузера этот фрагмент кода?

Если вы также, как и я, прочитали кучу статей про Event Loop, Main Thread, таски, микротаски и прочее, но затрудняетесь ответить на вопросы выше — эта статья для вас.

Итак, приступим. Код каждой HTML-страницы в браузере выполняется в Main Thread. Main Thread — это основной поток, где браузер выполняет JS, делает перерисовки, обрабатывает пользовательские действия и многое другое. По сути, это то место, где движок JS интегрирован в браузер.

Проще всего разобраться, глядя на схему:

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop
Рисунок 1

Мы видим, что единственное место, через которое задачи могут попасть в Call Stack и выполниться — это Event Loop. Представьте, что вы оказались на его месте. И ваша работа успевать ‘разгребать’ задачи. Задачи могут быть двух типов:

Конечно, первое, что приходит в голову — задать каждому заказчику приоритет, и выстроить их в очередь. Второе — определить, как именно будут обрабатываться задачи от каждого заказчика — по одной, все сразу или, может быть, пачками.

Взглянем на эту схему:

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop
Рисунок 2

На основе этой схемы строится вся работа Event Loop.
После того как мы начали выполнять какой-либо script, в очередь Tasks ставится задача с выполнением этого скрипта. По мере выполнения этого кода, нам встречаются задачи от разных заказчиков, которые ставятся в соответствующие очереди. После того как завершается задача по выполнению скрипта (задача от Tasks), Event Loop идет к Microtasks (после задачи от Tasks Event Loop берет задачи от Microtasks). У него Event Loop берет задачи до тех пор, пока они не закончатся. Это значит, что если время их добавления равно времени их выполнения, то Event Loop будет бесконечно их разгребать.
Далее он идет к Render и выполняет задачи от него. Задачи от Render оптимизируются браузером и, если он посчитает, что в этом цикле не нужно ничего перерисовывать, то Event Loop просто пойдет дальше. Далее Event Loop снова берет задачи от Tasks и просит у него только одну, первую в очереди задачу, передает ее в CallStack и идет дальше по циклу.

Если у кого-то из заказчиков не оказалось задач, то Event Loop просто идет к следующему. И, наоборот, если у заказчика задачи занимают много времени, то остальные заказчики будут ждать своей очереди. А если задачи от какого-то заказчика оказались бесконечными, то Call Stack переполняется, и браузер начинает ругаться:

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop
Рисунок 3

Теперь, когда мы поняли как работает Event Loop, пришло время разобраться, что будет после выполнения фрагментов кода в начале этой статьи.

Мы видим, что функция foo вызывает сама себя рекурсивно через setTimeout внутри, но при каждом вызове она создает задачу заказчика Tasks. Как мы помним, в цикле Event Loop при выполнении очереди задач от Tasks берет только 1 задачу в цикл. И далее происходит выполнение задач от Microtasks и Render. Поэтому этот фрагмент кода не заставит Event Loop страдать и вечно разгребать его задачи. Но будет подкидывать новую задачу для заказчика Tasks на каждом круге.

Давайте попробуем выполнить этот скрипт в браузере Google Chrome. Для этого я создал простой HTML-документ и подключил в нем script.js с этим фрагментом кода. После открытия документа заходим в инструменты разработчика, и открываем вкладку Perfomance и жмем там кнопку ‘start profiling and reload page’:

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop
Рисунок 4

Видим, что задачи от Tasks выполняются по одной в цикл, примерно раз в 4ms.

Рассмотрим вторую задачку:

Здесь мы видим тоже самое, что и в примере выше, но вызов foo добавляет задачи от Microtasks, а они выполняются все, пока не закончатся. А это значит, что пока Event Loop не закончит их, перейти к следующему заказчику он не сможет 🙁 И мы видим снова грустную картинку.

Взглянем на это в интрументах разработчкика:

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop
Рисунок 5

Мы видим, что микротаски выполняются примерно раз в 0.1ms, и это в 40 раз быстрее, чем очередь Tasks. Все потому, что они выполняются все и сразу. В нашем примере очередь движется бесконечно. Для визуализации я уменьшил ее до 100 000 итераций.

Надеюсь, эта статья была вам полезной, и теперь вы понимаете, как работает Event Loop, и что ‘творится’ в примерах кода выше.

Всем пока 🙂 И до новых встреч. Если вам понравилось, ставьте лайки и подписывайтесь на мой канал 🙂

Источник

Параллельная модель и цикл событий.

Параллелизм/Многопоточность в JavaScript работает за счёт цикла событий (event loop), который отвечает за выполнение кода, сбора и обработки событий и выполнения под-задач из очереди (queued sub-tasks). Эта модель весьма отличается от других языков программирования, таких как C и Java.

Концепция жизненного цикла

В следующей секции объясняется теоретическая модель. Современные JavaScript движки внедряют/имплементируют и существенно оптимизируют этот процесс.

Визуальное представление

Что такое event loop. Смотреть фото Что такое event loop. Смотреть картинку Что такое event loop. Картинка про Что такое event loop. Фото Что такое event loop

Для лучшего визуального представления работы Event loop, Вы можете ознакомиться с данным видео: https://www.youtube.com/watch?v=8aGhZQkoFbQ&t=389s

Объекты размещаются в куче. Куча — это просто имя для обозначения большой неструктурированной области памяти.

Очередь

Среда выполнения JavaScript содержит очередь задач. Эта очередь — список задач, подлежащих обработке. Каждая задача ассоциируется с некоторой функцией, которая будет вызвана, чтобы обработать эту задачу.

Когда стек полностью освобождается, самая первая задача извлекается из очереди и обрабатывается. Обработка задачи состоит в вызове ассоциированной с ней функции с параметрами, записанными в этой задаче. Как обычно, вызов функции создаёт новый контекст выполнения и заносится в стек вызовов.

Обработка задачи заканчивается, когда стек снова становится пустым. Следующая задача извлекается из очереди и начинается её обработка.

Цикл событий

Модель событийного цикла ( event loop ) называется так потому, что отслеживает новые события в цикле:

queue.waitForMessage ожидает поступления задач, если очередь пуста.

Запуск до завершения

Каждая задача выполняется полностью, прежде чем начнёт обрабатываться следующая. Благодаря этому мы точно знаем: когда выполняется текущая функция – она не может быть приостановлена и будет целиком завершена до начала выполнения другого кода (который может изменить данные, с которыми работает текущая функция). Это отличает JavaScript от такого языка программирования как C. Поскольку в С функция, запущенная в отдельном потоке, в любой момент может быть остановлена, чтобы выполнить какой-то другой код в другом потоке.

У данного подхода есть и минусы. Если задача занимает слишком много времени, то веб-приложение не может обрабатывать действия пользователя в это время (например, скролл или клик). Браузер старается смягчить проблему и выводит сообщение «скрипт выполняется слишком долго» («a script is taking too long to run») и предлагает остановить его. Хорошей практикой является создание задач, которые исполняются быстро, и если возможно, разбиение одной задачи на несколько мелких.

Добавление событий в очередь

Вызов setTimeout добавит событие в очередь по прошествии времени, указанного во втором аргументе вызова. Если очередь событий на тот момент будет пуста, то событие обработается сразу же, в противном случае событию функции setTimeout придётся ожидать завершения обработки остальных событий в очереди. Именно поэтому второй аргумент setTimeout корректно считать не временем, через которое выполнится функция из первого аргумента, а минимальное время, через которое она сможет выполниться.

Нулевые задержки

Нулевая задержка не даёт гарантии, что обработчик выполнится через ноль миллисекунд. Вызов setTimeout с аргументом 0 (ноль) не завершится за указанное время. Выполнение зависит от количества ожидающих задач в очереди. Например, сообщение »this is just a message» из примера ниже будет выведено на консоль раньше, чем произойдёт выполнение обработчика cb1. Это произойдёт, потому что задержка – это минимальное время, которое требуется среде выполнения на обработку запроса.

Связь нескольких потоков между собой

Web Worker или кросс-доменный фрейм имеют свой собственный стек, кучу и очередь событий. Два отдельных событийных потока могут связываться друг с другом, только через отправку сообщений с помощью метода postMessage. Этот метод добавляет сообщение в очередь другого, если он конечно принимает их.

Никогда не блокируется

Очень интересное свойство цикла событий в JavaScript, что в отличие от множества других языков, поток выполнения никогда не блокируется. Обработка I/O обычно осуществляется с помощью событий и колбэк-функций, поэтому даже когда приложение ожидает запрос от IndexedDB или ответ от XHR, оно может обрабатывать другие процессы, например пользовательский ввод.

Существуют хорошо известные исключения как alert или синхронный XHR, но считается хорошей практикой избегать их использования.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *