Я сталкивался с этой концепцией довольно много раз с тех пор, как изучил этот сложный язык, и до сих пор не могу полностью понять эту концепцию. Этот блог разбивает цикл событий на многочисленные аналогии, чтобы мы могли понять все это. Во-первых, JavaScript — это однопоточный язык, что означает, что он имеет только один стек вызовов, который используется для выполнения программы.

Вот аналогия с YouTube-канала Java Brains, которая максимально упрощает этот однопоточный процесс:

Представьте себе ресторан с одним официантом. В ресторане гости сидят за несколькими столами, и официант должен обслуживать их всех. Если события разнесены, у него нет проблем. Таким образом, если у одного стола есть вопрос, он предоставит помощь, а если у другого стола возникнет вопрос после того, как он обслужит первый стол, он сможет оказать помощь. Без проблем. Но что делать, если в ресторане многолюдно? Если стол зовет официанта, задает вопрос, а затем вскоре после того, как другой столик вызывает официанта, пока он помогает первому столу, что он должен делать? Он не может просто игнорировать текущий стол, за которым он ассистирует. Он сделает мысленную пометку, объясняя, что второй стол нуждается в помощи. Третий гость подзывает его, и он тоже отмечает это. Как только он закончит с конкретным столом, он прокручивает свой разум, чтобы определить, какой стол нуждается в помощи в том порядке, в котором они просили. Первый пришел первый обслужен.

В самой простой форме это цикл событий. Один единственный поток выполнения, который выполняет все, что находится на странице. Пример из реальной жизни: щелкнуть кнопку на веб-странице, а затем сразу же нажать другую кнопку. Поток не собирается просто бросать то, что он делает, и переходить ко второй кнопке. Он делает «мысленную пометку» и выполняет первый щелчок, а затем второй щелчок. Мысленная заметка называется очередью событий. Очередь событий — это список дел для потока, который будет выполняться до завершения.

А что, если есть функции обратного вызова? Здесь цикл обработки событий может немного запутаться. Если есть несколько функций, это зависит от того, являются ли они синхронными или асинхронными функциями. Вернемся к примеру с официантом. Если официант забирает событие из очереди и подходит к столу. Человек за столом начинает отдавать официанту свой заказ. клиент проверяет со своим компаньоном, чтобы увидеть, что они хотят. Официант не собирается уходить, пока другой клиент не объяснит свой заказ. Официант должен дождаться получения заказа от других клиентов, прежде чем он пойдет дальше. Это синхронно. Официант ждет, пока не будет дан ответ на последний вопрос и заказ будет полностью отдан, прежде чем двигаться дальше.

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

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

Все еще со мной….?

Хорошо, потому что теперь пришло время для асинхронных событий. Вернемся к старой доброй аналогии с официантом:

Теперь у нас есть повар. Официант принимает заказ со столика 3 и передает его шеф-повару. Официант не собирается сидеть и ждать, пока повар приготовит заказ. Официант — это асинхронное событие. Он пойдет к другим гостям и скажет шеф-повару перезвонить ему, когда заказ будет готов. Итак, официант переходит к следующему событию в очереди событий. Теперь представьте, что он разговаривает с кем-то за столом 1, а кто-то за столом 2 поднимает руку. При этом заказ на стол 3 полностью готовится шеф-поваром. Что теперь? Он строго работает в порядке поступления. Затем он добавляет в очередь событий запрос второго стола с последующей подачей блюда на стол 3.

Когда происходит асинхронный вызов, часто с ним связан обратный вызов. Затем асинхронный вызов завершает обратный вызов, который не выполняется немедленно, поскольку помещается в конец очереди событий.

Вот пример с YouTube-канала Java Brains:

let order1 = takeOrder(table1);
chef.handOver(order, блюдо =› serveTable(блюдо));
let order2 = takeOrder(table2);

Сначала официант синхронно принимает заказ со стола 1. Он передает заказ шеф-повару с обратным вызовом, чтобы сказать ему подавать блюдо, как только оно будет приготовлено. Официант не ждет. Он подходит, чтобы взять заказ со стола 2. Когда он принимает заказ, повар перезванивает официанту, чтобы сообщить ему, что заказ 1 готов. Обратный вызов не выполняется немедленно, он идет в конец очереди событий, чтобы официант мог добраться до него, как только он закончит текущую задачу.

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

Все еще в замешательстве? Добро пожаловать в клуб.

Вот визуальное представление цикла событий JavaScript:

Этот блог либо прояснил для вас что-то, либо закопал вас глубже в яму. В любом случае, спасибо за чтение!