Что такое цикл событий
Как управлять event loop в JavaScript. Часть 1
Event loop (событийные циклы) — важная часть архитектуры JavaScript. Мы попросили эксперта объяснить, как в этом разобраться.
Понимание работы event loop — неотъемлемый пункт карьерного роста middle-разработчика. Если хотите знать всё о JavaScript, ждём вас на курсе «Frontend-разработчик». Вы научитесь верстать сайты и создавать интерфейсы, а на выходе получите два готовых проекта в портфолио.
А сейчас о событийных циклах рассказывает Александр Кузьмин — ведущий программист с десятилетним опытом во frontend, руководитель отдела клиентской разработки компании IT-Park. Передаём слово эксперту.
Автор в сфере IT, digital, экономики и финансов. Ведет некоммерческий проект для начинающих писателей «ЛитЦех».
Асинхронность в JavaScript
У каждого языка свой подход к параллельному вычислению данных. Например, в языках типа C++ оно передаётся в отдельный поток или даже процесс, который выполняется на другой машине.
ведущий программист, руководитель отдела клиентской разработки компании IT-Park
Если нужно сообщить потоку что-то вроде «посчитай вот это и положи результат в базу данных, а я когда-нибудь приду за ними», мы имеем дело с асинхронными операциями.
Это значит, что код, который их вызвал, не ждёт завершения выполнения, а продолжает исполняться дальше. Если же мы хотим дождаться результата, у многих современных языков есть операторы async и await для синхронизации исполняемого кода.
В JavaScript асинхронность — основной инструмент. Во времена до появления Node.JS он был практически единственным языком исполнения сценариев на клиенте в вебе (Internet Explorer поддерживал VB Script, но его никто не использовал). Сейчас невозможно представить интернет, где все запросы на сервер отправлялись бы с перезагрузкой страницы. Напротив, мы пришли к одностраничному вебу, в котором на стороне клиента происходит разрешение адресов страниц и отображение соответствующего контента.
Любые данные от сервера запрашиваются асинхронно: отправляется запрос (XMLHttpRequest или XHR), и код не ждёт его возвращения, продолжая выполняться. Когда же сервер отвечает, объект XHR получает уведомление об этом и запускает функцию обратного вызова — callback, который передали в него перед отправкой запроса.
Если придётся ждать, пока запрос придёт, JavaScript перестанет принимать любые события, а страница зависнет. Чтобы пользователь спокойно использовал веб-приложение, запрос выводят из текущего контекста выполнения. Операции, результата которых приходится ждать, прежде чем продолжать выполнение кода, называются блокирующими. О них — во второй части статьи.
Суть кроется в устройстве языка:
JavaScript ориентирован на общение с пользователем, чтобы тот мог запускать несколько событий одновременно.
Если правильно использовать инструменты языка, то выполнение кода, которое происходит последовательно и в одном потоке, никак не мешает приёму событий и реакции на них — человек спокойно работает с интерфейсом, не замечая лагов, сбоев и зависаний.
Event loop в JavaScript — менеджер асинхронных вызовов
Чтобы этот хитрый процесс слаженно работал, в JavaScript реализован механизм для управления очерёдностью исполнения кода. Поскольку это однопоточный язык, возникла необходимость «вклиниваться» в текущий контекст исполнения. Этот механизм называется event loop — событийный цикл.
С английского loop переводится как «петля», что отлично отражает смысл: мы имеем дело с закольцованной очередью.
Event loop регулирует последовательность исполнения контекстов — стек. Он формируется, когда сработало событие или была вызвана функция. Реакция на событие помещается в очередь исполнения, в event loop, который последовательно, с каждым циклом выполняет попадающий в него код. При этом привязанная к событию функция вызывается следующей после текущего контекста исполнения.
В JavaScript постоянно работают связанные между собой синхронная и асинхронная очереди выполнения. Синхронная — стек — формирует очередь и пробрасывает в асинхронную — event loop — вызовы функций, которые будут выполнены после текущего запланированного исполняемого контекста.
Чтобы данные находились в консистентном состоянии, каждая функция должна быть выполнена до конца. Это обусловлено однопоточностью JavaScript и некоторыми другими особенностями, например характерными для функциональных языков программирования замыканиями. Поэтому единственный поток представлен в виде очереди контекстов исполнения, в которой и происходит «вклинивание» функций, прошедших через цикл событий.
Справка
В JavaScript существует понятие «контекст функции». Но есть и другой термин — «контекст исполнения». Это тело функции со всеми переменными и другими функциями, которое называют «область видимости», с английского — “scope”. Важно не путать понятия, это принципиально разные вещи.
Как формируется контекст исполнения
JavaScript — интерпретируемый язык. Это значит, что любой код проходит через интерпретатор, который исполняет его построчно. Но и здесь есть нюансы.
Как только скрипт попадает в интерпретатор, формируются глобальный контекст и глобальная область видимости, в которой держится Variable Object, или VO — объект переменных.
Он формируется из переменных вида Function Declaration и атрибутов функции по следующему принципу. Интерпретатор считывает код и находит все объявления:
Это складывается в VO текущего контекста исполнения. Затем берётся Variable Object внешней области видимости и к нему добавляется сформированный выше VO. Сверху он дополняется параметрами функции и их значениями на момент исполнения.
При этом нет разницы, в каком месте функции они определяются. Переменная может быть определена в любой части кода, как и функция.
VO этого скрипта формируется:
Затем скрипт начнет исполняться по следующему сценарию:
Теперь перепишем скрипт, добавив setTimeout с нулевым тайм-аутом у вызова функции:
На первый взгляд может показаться, что ничего не изменится и функция func будет выполнена без задержки. Но это не так. На самом деле произойдёт следующее:
Почему так происходит?
Всему виной setTimeout, очевидно. Он выводит контекст исполнения функции из синхронного потока, помещая его в event loop. То же самое происходит и с регистрацией событий. Мы можем подписаться на событие при помощи функции addEventListener. Передавая функцию обратного вызова — callback, добавляем её в список функций, которые должны быть вызваны при срабатывании этого события.
Допустим, мы хотим нажатием на кнопку перекрасить её в красный цвет. Код, который это выполняет, выглядит так:
Переданная функция всегда выполняется через event loop. При возникновении события в цикл последовательно попадут все привязанные к нему функции. Для каждой будет формироваться контекст исполнения, который будет запущен следом за текущим.
Если в процессе будет вызвано ещё одно событие, его коллбэки будут вставать в очередь по тому же принципу, возможно, перемежаясь с контекстами исполнения первого события.
Более сложный пример: есть две кнопки, первая перекрашивает фон страницы в красный цвет, а вторая — в жёлтый, но у второй перекрашивание фона завёрнуто в setTimeout с нулевой задержкой. И мы вручную вызываем событие нажатия сначала на жёлтую кнопку, а потом — на красную.
Что происходит в такой ситуации, исходя из того, что мы рассмотрели выше?
Обратите внимание, что исполнение коллбэков событий click на кнопках при вызове из кода происходит сразу же, не попадая в event loop: setTimeout с нулевой задержкой отложил перекраску фона в жёлтый, но функция сама была исполнена в момент вызова.
Это происходит из-за того, что события из кода не требуется выполнять асинхронно. Действительно, в такой ситуации мы находимся в предсказуемом окружении, тогда как пользовательские события могут случаться в любой момент.
Это приводит к теме следующей части: об управлении событийным циклом и тем, что будет в него попадать. Подробно рассмотрим, как грамотно формировать цепочки последовательных асинхронно вызванных контекстов вызова, уменьшая вычислительную сложность каждого из них, освобождая поток и позволяя пользовательским событиям «вклиниваться» в очередь исполнения.
Если вы хотите знать все о JavaScript, ждем вас на курсе «Профессия frontend-разработчик». Вы научитесь верстать сайты и создавать интерфейсы, а на выходе получите два готовых проекта в портфолио.
Что ты такое, Event Loop? Или как устроен цикл событий в браузере Chrome
Как думаете, что произойдет, если запустить в консоли браузера этот фрагмент кода?
Если вы также, как и я, прочитали кучу статей про Event Loop, Main Thread, таски, микротаски и прочее, но затрудняетесь ответить на вопросы выше — эта статья для вас.
Итак, приступим. Код каждой HTML-страницы в браузере выполняется в Main Thread. Main Thread — это основной поток, где браузер выполняет JS, делает перерисовки, обрабатывает пользовательские действия и многое другое. По сути, это то место, где движок JS интегрирован в браузер.
Проще всего разобраться, глядя на схему:
Рисунок 1
Мы видим, что единственное место, через которое задачи могут попасть в Call Stack и выполниться — это 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 переполняется, и браузер начинает ругаться:
Рисунок 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’:
Рисунок 4
Видим, что задачи от Tasks выполняются по одной в цикл, примерно раз в 4ms.
Рассмотрим вторую задачку:
Здесь мы видим тоже самое, что и в примере выше, но вызов foo добавляет задачи от Microtasks, а они выполняются все, пока не закончатся. А это значит, что пока Event Loop не закончит их, перейти к следующему заказчику он не сможет 🙁 И мы видим снова грустную картинку.
Взглянем на это в интрументах разработчкика:
Рисунок 5
Мы видим, что микротаски выполняются примерно раз в 0.1ms, и это в 40 раз быстрее, чем очередь Tasks. Все потому, что они выполняются все и сразу. В нашем примере очередь движется бесконечно. Для визуализации я уменьшил ее до 100 000 итераций.
Надеюсь, эта статья была вам полезной, и теперь вы понимаете, как работает Event Loop, и что ‘творится’ в примерах кода выше.
Всем пока 🙂 И до новых встреч. Если вам понравилось, ставьте лайки и подписывайтесь на мой канал 🙂
Руководство по Node.js, часть 6: цикл событий, стек вызовов, таймеры
Цикл событий
Если вы хотите разобраться с тем, как выполняется JavaScript-код, то цикл событий (Event Loop) — это одна из важнейших концепций, которую необходимо понять. Здесь мы поговорим о том, как JavaScript работает в однопоточном режиме, и о том, как осуществляется обработка асинхронных функций.
Я многие годы занимался разработкой на JavaScript, но не могу сказать, что полностью понимал то, как всё функционирует, так сказать, «под капотом». Программист вполне может не знать о тонкостях устройства внутренних подсистем среды, в которой он работает. Но обычно полезно иметь хотя бы общее представление о подобных вещах.
JavaScript-код, который вы пишете, выполняется в однопоточном режиме. В некий момент времени выполняется лишь одно действие. Это ограничение, на самом деле, является весьма полезным. Это значительно упрощает то, как работают программы, избавляя программистов от необходимости решать проблемы, характерные для многопоточных сред.
Фактически, JS-программисту нужно обращать внимание только на то, какие именно действия выполняет его код, и стараться при этом избежать ситуаций, вызывающих блокировку главного потока. Например — выполнения сетевых вызовов в синхронном режиме и бесконечных циклов.
Обычно в браузерах, в каждой открытой вкладке, имеется собственный цикл событий. Это позволяет выполнять код каждой страницы в изолированной среде и избегать ситуаций, когда некая страница, в коде которой имеется бесконечный цикл или выполняются тяжёлые вычисления, способна «подвесить» весь браузер. Браузер поддерживает работу множества одновременно существующих циклов событий, используемых, например, для обработки вызовов к различным API. Кроме того, собственный цикл событий используется для обеспечения работы веб-воркеров.
Самое важное, что надо постоянно помнить JavaScript-программисту, заключается в том, что его код использует собственный цикл событий, поэтому код надо писать с учётом того, чтобы этот цикл событий не заблокировать.
Блокировка цикла событий
Любой JavaScript-код, на выполнение которого нужно слишком много времени, то есть такой код, который слишком долго не возвращает управление циклу событий, блокирует выполнение любого другого кода страницы. Подобное приводит даже к блокировке обработки событий пользовательского интерфейса, что выражается в том, что пользователь не может взаимодействовать с элементами страницы и нормально с ней работать, например — прокручивать.
Практически все базовые механизмы обеспечения ввода-вывода в JavaScript являются неблокирующими. Это относится и к браузеру и к Node.js. Среди таких механизмов, например, можно отметить средства для выполнения сетевых запросов, используемые и в клиентской и в серверной средах, и средства для работы с файлами Node.js. Существуют и синхронные способы выполнения подобных операций, но их применяют лишь в особых случаях. Именно поэтому в JavaScript огромное значение имеют традиционные коллбэки и более новые механизмы — промисы и конструкция async/await.
Стек вызовов
Стек вызовов (Call Stack) в JavaScript устроен по принципу LIFO (Last In, First Out — последним вошёл, первым вышел). Цикл событий постоянно проверяет стек вызовов на предмет того, имеется ли в нём функция, которую нужно выполнить. Если при выполнении кода в нём встречается вызов некоей функции, сведения о ней добавляются в стек вызовов и производится выполнение этой функции.
Если даже раньше вы не интересовались понятием «стек вызовов», то вы, если встречались с сообщениями об ошибках, включающими в себя трассировку стека, уже представляете себе, как он выглядит. Вот, например, как подобное выглядит в браузере.
Сообщение об ошибке в браузере
Браузер, при возникновении ошибки, сообщает о последовательности вызовов функций, сведения о которых хранятся в стеке вызовов, что позволяет обнаружить источник ошибки и понять, вызовы каких функций привели к сложившейся ситуации.
Теперь, когда мы в общих чертах поговорили о цикле событий и о стеке вызовов, рассмотрим пример, иллюстрирующий выполнение фрагмента кода, и то, как этот процесс выглядит с точки зрения цикла событий и стека вызовов.
Цикл событий и стек вызовов
Вот код, с которым мы будем экспериментировать:
Если этот код выполнить, в консоль попадёт следующее:
Изменение состояния стека вызовов при выполнении исследуемого кода
Цикл событий, на каждой итерации, проверяет, есть ли что-нибудь в стеке вызовов, и если это так — выполняет это до тех пор, пока стек вызовов не опустеет.
Итерации цикла событий
Постановка функции в очередь на выполнение
Вышеприведённый пример выглядит вполне обычным, в нём нет ничего особенного: JavaScript находит код, который надо выполнить, и выполняет его по порядку. Поговорим о том, как отложить выполнение функции до момента очистки стека вызовов. Для того чтобы это сделать, используется такая конструкция:
То, что выведет этот код, возможно, покажется неожиданным:
Вот как теперь будет выглядеть стек вызовов.
Изменение состояния стека вызовов при выполнении исследуемого кода
Вот в каком порядке теперь будут выполняться функции в нашей программе.
Итерации цикла событий
Почему всё происходит именно так?
Очередь событий
Цикл событий отдаёт приоритет тому, что находится в стеке вызовов. Сначала он выполняет всё, что ему удаётся найти в стеке, а после того, как стек оказывается пустым, переходит к обработке того, что находится в очереди событий.
Очередь заданий ES6
В ECMAScript 2015 (ES6) была введена концепция очереди заданий (Job Queue), которой пользуются промисы (они тоже появились в ES6). Благодаря очереди заданий результатом выполнения асинхронной функции можно воспользоваться настолько быстро, насколько это возможно, без необходимости ожидания очищения стека вызовов.
Если промис разрешается до окончания выполнения текущей функции, соответствующий код будет выполнен сразу после того, как текущая функция завершит работу.
Я обнаружил интересную аналогию для того, о чём мы сейчас говорим. Это можно сравнить с американскими горками в парке развлечений. После того, как вы прокатились на горке и хотите сделать это ещё раз, вы берёте билет и становитесь в хвост очереди. Так работает очередь событий. А вот очередь заданий выглядит иначе. Эта концепция похожа на льготный билет, который даёт вам право совершить следующую поездку сразу после того, как вы закончили предыдущую.
Рассмотрим следующий пример:
Вот что будет выведено после его выполнения:
То, что тут можно видеть, демонстрирует серьёзное различие промисов (и конструкции async/await, которая на них основана) и традиционных асинхронных функций, выполнение которых организуется посредством setTimeout() или других API используемой платформы.
process.nextTick()
Предположим, цикл событий занят выполнением кода текущей функции. Когда эта операция завершается, JavaScript-движок выполнит все функции, переданные process.nextTick() в ходе выполнения предыдущей операции. Используя этот механизм, мы стремимся к тому, чтобы некая функция была бы выполнена асинхронно (после текущей функции), но как можно скорее, без постановки её в очередь.
setImmediate()
Таймеры
▍Функция setTimeout()
Напомним, что при вызове функции setTimeout() ей передают коллбэк и время, в миллисекундах, по прошествии которого будет вызван коллбэк. Рассмотрим пример:
Здесь мы передаём setTimeout() новую функцию, тут же описываемую, но здесь можно использовать и существующую функцию, передавая setTimeout() её имя и набор параметров для её запуска. Выглядит это так:
Функция setTimeout() возвращает идентификатор таймера. Обычно он не используется, но его можно сохранить, и, при необходимости, удалить таймер, если в запланированном выполнении коллбэка больше нет необходимости:
▍Нулевая задержка
Такой код выведет следующее:
▍Функция setInterval()
Коллбэк, переданный функции, показанной выше, будет вызываться каждые 2 секунды. Для того чтобы предусмотреть возможность остановки этого процесса, нужно получить идентификатор таймера, возвращаемый setInterval() и воспользоваться командой clearInterval() :
Распространённой методикой является вызов clearInterval() внутри коллбэка, переданного setInterval() при выполнении некоего условия. Например, следующий код будет периодически запускаться до тех пор, пока свойство App.somethingIWait не примет значение arrived :
▍Рекурсивная установка setTimeout()
Функция setInterval() будет вызывать переданный ей коллбэк каждые n миллисекунд, не заботясь о том, завершилось ли выполнение этого коллбэка после его предыдущего вызова.
Периодически вызываемый коллбэк, каждый сеанс выполнения которого занимает одно и то же время, укладывающееся в промежуток между вызовами
Периодически вызываемый коллбэк, каждый сеанс выполнения которого занимает разное время, укладывающееся в промежуток между вызовами
Периодически вызываемый коллбэк, каждый сеанс выполнения которого занимает разное время, которое иногда не укладывается в промежуток между вызовами
При таком подходе можно реализовать следующий сценарий:
Рекурсивный вызов setTimeout() для планирования выполнения коллбэка
Как устроен Event Loop в JavaScript: параллельная модель и цикл событий
В Event Loop в языке JavaScript заключается секрет асинхронного программирования. Сам по себе JS является однопоточным, но при использовании нескольких умных структур данных можно создать иллюзию многопоточности (параллельная модель). Как это происходит, расскажем в этой статье.
Код JavaScript работает только в однопоточном режиме. Это означает, что в один и тот же момент может происходить только одно событие. С одной стороны это хорошо, так как такое ограничение значительно упрощает процесс программирования, здесь не возникает проблем параллелизма. Но, как правило, в большинстве браузеров в каждой из вкладок существует свой цикл событий. Среда управляет несколькими параллельными циклами.
Общим знаменателем для всех сред является встроенный механизм, называемый Event Loop JavaScript, который обрабатывает выполнение нескольких фрагментов программы, вызывая каждый раз движок JS.
Какова идея цикла событий?
Существует бесконечный цикл событий, в котором JavaScript движок ожидает свою задачу, выполняет ее и ждет новую. Алгоритм работы движка мы можем видеть при просмотре любой веб-страницы. Он включается в работу тогда, когда необходимо обработать какое-либо событие или скрипт. Схема работы выглядит следующим образом:
Визуально процесс можно изобразить так:
Отслеживание новых событий в цикле:
Если в очереди нет задач, queue.waitForMessage ожидает их поступления.
Как события добавляются в очередь
Все события в браузерах постоянно добавляются в очередь, если они произошли или имеют свой обработчик. setTimeout может добавлять событие в очередь не сразу, а по прошествии указанного времени. Если на данный момент в очереди нет событий, то оно поступит в обработку сразу.
Когда операция setTimeout обрабатывается в стеке, она отправляется соответствующему API, который ожидает до указанного времени, чтобы отправить эту операцию в обработку. Среда управляет несколькими параллельными циклами событий, например, для обработки вызовов API. Веб-воркеры также работают в собственном цикле событий.
Операция отправляется в очередь событий. Следовательно, у нас есть циклическая схема для выполнения асинхронных операций в JavaScript. Сам язык является однопоточным, но API-интерфейсы браузера действуют как отдельные потоки.
Цикл событий постоянно проверяет, пуст ли стек вызовов. Если он пуст, новые функции добавляются из очереди событий. Если это не так, то выполняется текущий вызов функции.
Давайте посмотрим, как отложить выполнение функции до тех пор, пока стек не очистится.
Пример использования setTimeout(() => <>), 0) заключается в том, чтобы вызвать функцию, но выполнить ее после выполнения всех остальных функций в коде.
bar, baz, foo — случайные имена.
При запуске кода сначала вызывается foo(). Внутри foo() мы сначала вызываем setTimeout, передавая bar в качестве аргумента, и инструктируем его таким образом, чтобы он запускался как можно быстрее, передавая 0 в качестве таймера. Затем мы вызываем baz().
Порядок функций в программе:
Почему так происходит?
Очередь событий
При вызове setTimeout(), браузер или Node.js запускают таймер. По истечении таймера (в нашем случае мы установили 0) в качестве тайм-аута, функция обратного вызова помещается в очередь событий.
Очередь событий также является местом, где инициированные пользователем события (клики мышью, ввод с клавиатуры и др.) помещаются в очередь до того, как код сможет на них отреагировать.
Event Loop отдает приоритет стеку вызовов. Сначала он обрабатывает все, что находит в стеке вызовов, а когда там ничего не остается, переходит к обработке очереди событий.
setTimeout с аргументом 0 не гарантирует, что обработка будет выполнена мгновенно. Все зависит от того, сколько задач в данный момент находится в очереди. В примере ниже ”message” будет выведена быстрее обработчика callback_1. Объясняется это тем, что задержка представляет собой минимальное время, необходимое среде на выполнение запроса.
Цикл событий в JavaScript отличается от других языков тем, что его поток выполнения никогда не блокируется, кроме некоторых исключений, таких как alert или синхронный HTTP-запрос, которые не рекомендуется использовать. Поэтому даже когда приложение ожидает запросы из хранилища или ответ с сервера, оно может обрабатывать другие процессы, например пользовательский ввод.
В заключение
Веб-сайты стали более интерактивными и динамичными, необходимость выполнения интенсивных операций стала все более актуальной (к примеру, выполнение внешних сетевых запросов для получения данных API). Чтобы обрабатывать эти операции, необходимо использование методов асинхронного программирования. Встроенный механизм Event Loop помогает JavaScript обрабатывать асинхронный код.
Простой метод измерения реальной скорости загрузки страниц у посетителей сайта
Что делать, если часть логики написана на PHP, а часть на NodeJS
Как можно закэшировать данные и выиграть в производительности
Как работает Server-Sent API с примерами
Примеры применения Javascript в Nginx’e
Как просто сделать удобный дебаг и не лазить в код или как бородатые хакеры перехватывают ajax-запросы, нарушая вашу безопасность.
В своем блоге индийский разработчик Шашват Верма (Shashwat Verma) рассказал, как преобразовать веб-сайт или веб-страницу в прогрессивное веб-приложение (PWA).