Методы setTimeout и setInterval в Javascript

Видеоурок к статье

 

Введение

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

 

 

Метод setTimeout

Метод setTimeout служит для того, чтобы вы смогли выполнить какой-нибудь Javascript сценарий с определенным интервалом.

 

Пример setTimeout:

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

 

Создайте html файл с содержимым:

 

В данном примере, мы создали структуру документа html, создали div с id settimeout и подключили библиотеку JQuery. 

Затем мы создали анонимный метод setTimeout и задали время выполнения через 10 секунд. Обратите внимание, что интервал задается в миллисекундах.

Внутри метода мы говорим, что хотим в div с id settimeout вставить html с текстом «Я появился через 10 секунд после загрузки страницы».

Стоит заметить, что setTimeout выполняется только один раз. 

 

Метод setInterval

В отличии от setTimeout, метод setInterval выполняется до тех пор, пока не будет вызван метод clearInterval

Для демонстрации метода setInterval, усложним немного пример. 

Наш пример будет состоять из двух  файлом ajax.php в котором мы будем хранить массив и возвращать его в формате json.

В файле index.html мы будем ajax’сом выводить новости из файла ajax.php с интервалом две секунды.

 

Файл ajax.php

 

В этом файле для примера мы будем хранить данные новостей. Первой строкой мы указываем кодировку utf-8 чтобы можно было корректно отображать русский язык. Далее мы создаем многомерный массив. И строкой json_encode мы преобразуем массив php в формат json для работы с этими данными в javascript.

 

Теперь дополните созданный файл из прошлого примера index.html следующим кодом:

 

Методом библиотеки Jquery $.ajax мы делаем фоновую загрузку данных из файла ajax.php. Далее мы создаем функцию setInterval где итеративно добавляем к переменной count единицу. Методом setInterval мы заменили цикл each, чтобы продемонстрировать setInterval.

Методом Jquery append добавляем к div c id setinterval данные. Когда новости заканчиваются, мы очищаем метод setInterval clearInterval, для того, чтобы он больше не выполнялся.  

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

 

Выводы

Методы setInterval и setTimeout используются очень часто в работе веб-программиста. 

Если у вас остались вопросы и непонятен принцип работы этих двух полезных методов javascript, посмотрите видеоурок статье.

Скачать примеры вы можете по этой ссылке. 

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





Читайте также


Что такое NodeJS и npm?

XAMPP — как установить и настроить на Mac (Mojave, Sierra)

Как самостоятельно изучить веб-программирование

Взломали Cкайп, что делать?

Что такое реферальный спам в Google Analytics

Постраничная ленивая загрузка (lazy load) постов

Как стать профессиональным веб-разработчиком

Реализация Lazy Load на Jquery

Три полезных CSS свойства для работы с изображениями

Массивы в Javascript

Что такое TypeScript

CSS3 — Эффект вращения

Sublime Text 3 — удобный редактор кода для веб-разработчиков

Установка и настройка веб-сервера для сайта в Ubuntu

Полезные приложения для веб-разработчиков в Google Chrome

Модальное окно на Jquery

GIT команды: Быстрый старт для новичков

Что такое конструктор в объектно-ориентированном программировании

Что такое объектно-ориентированное программирование

Как отправить форму без перезагрузки страницы (AJAX)

Как быстро создать сайт и привлечь поисковый трафик

Bitbucket: Крутой облачный GIT репозиторий

Javascript: Классы в Javascript

Что такое веб-хостинг и как выбрать хостинг для сайта

SQL запросы: Основы администрирования MySQL

Команды Linux: оболочка BASH

Joomla CMS: Преимущества и недостатки

Качественный сайт: семь ключевых свойств



Все материалы с сайта wh-db. com и ru.wh-db.com защищены авторским правом. Копирование, публикация, продажа и распространение материала строго запрещены.

Please enable JavaScript to view the comments powered by Disqus.

Как в JavaScript работает `setTimeout` и что происходит если он установлен на 0?

Чтобы разобраться в том как работает setTimeout и функции которые вызываются в нем, нужно разобраться в том как вообще работает JavaScript. Разобравшись в этом, вы станете куда увереннее пользоваться джаваскриптом.

Простейший пример setTimeout:

setTimeout(коллбек, задержка) означает «вызови обратный вызов (коллбек, это может быть какая либо функция, в нашем случае это функция с console.log("какой-то обратный вызов")) после заданной задержки». Но что происходит если мы задаем задержку в 0? Получается, он должен вызвать коллбек сразу же? Может показаться что да, но все сложнее.

Подумайте, что сделает этот код (к слову, подобные вопросы любят задавать на интервью):

В какой последовательности будут отображены эти сообщения?

Отобразит он вот это:

Почему-то JavaScript прописывает раз, два, три, и только после этого прописывает setTimeout заданный в 0.

Можно пойти дальше, и, скажем, написать такой код с несколькими циклами:

Но в результате setTimeout заданный в 0 все равно окажется в конце:

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

Как setTimeout работает в JavaScript

Важно понимать что JavaScript это однопоточный (single-threaded) язык программирования. Про это часто пишут и говорят, но зачем-то эту концепцию излишне усложняют, хотя, вообще-то, все очень просто:

JavaScript все делает в одном потоке. Он последовательно берет задачи из специального списка, который называется Call Stack. Проще всего воспринимать этот список как TODO лист, задачи из которого JavaScript и выполняет.

Именно по таким правилам живут эти console.log:

JavaScript получает задачу написать в консоли «раз», эта задача появляется вверху списка, он выполняет ее и убирает ее из списка.

Все становиться сложнее когда появляются асинхронные операции, к которым, собственно, и относиться setTimeout.

А сейчас, страшная тайна, меня это в свое время удивило, но вообще-то setTimeout это не часть JavaScript движка! Это Web API, браузеры (и, скажем, nodejs), просто, по доброте душевной, позволяют нам использовать setTimeout, именно браузеры обрабатывают и запускают указанную в setTimeout задержку. JavaScript работает c этим API, но это просто часть его окружения, это не сам движок.

Так как же будет обрабатываться наш код? Когда мы вызываем setTimeout:

Сообщение добавляется в очередь с указанным обратным вызовом. А остальная часть кода:

Продолжает выполняться синхронно. Как только этот код полностью завершится, цикл обработки событий (event loop — эвент луп) начнет опрашивать очередь сообщений на наличие следующего сообщения. Он найдет сообщение с обратным вызовом (callback) setTimeout, после чего начнется его обработка (выполнение обратного вызова).

Так что же все таки происходит, если задержка установлена ​​на 0? Новое сообщение будет немедленно добавлено в очередь, но обработано оно будет только после того как текущий исполняемый код будет завершен и все ранее добавленные сообщения будут обработаны.

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

Получается, затронув такой, как казалось, простой вопрос, почему setTimeout с задержкой заданной в 0 не исполняется сразу же, нам пришлось иметь дело и с обратными вызовами, и с внешним окружением (Web API), и с эвент лупом, и тд.

Дальнейшее изучение

Изначально эта статья была переводом вопроса и ответа со stackoverflow: What is setTimeout doing when set to 0 milliseconds?, но через какое-то время все переросло в отдельную статью. Но частично это все еще перевод этого вопроса и ответа.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/

Крутое видео на YouTube про то как работает эвент луп: What the heck is the event loop anyway? | Philip Roberts | JSConf EU

Визуализация того как работает JavaScript: http://latentflip.com/loupe

Еще один визуализатор для JavaScript, но визуализирующий контексты, замыкания и области видимости: https://tylermcginnis.com/javascript-visualizer/

Внутреннее устройство JavaScript: движок JavaScript, среда выполнения и веб-API setTimeout | Автор Rupesh Mishra

Подробное описание всех основных компонентов, участвующих в выполнении кода JavaScript

Фото Брама Науса на Unsplash

временная среда и браузер. Это будет подробный обзор всех основных компонентов, участвующих в выполнении кода JavaScript. Мы обсудим следующие компоненты:

  1. Механизм JavaScript
  2. Среда выполнения JavaScript
  3. Цикл событий, таблица событий и очередь обратного вызова или очередь сообщений
  4. setTimeout внутренняя работа функции

Начнем с механизма JavaScript.

Небольшой совет, прежде чем мы начнем: Используйте Bit (Github), чтобы превратить ваш JS-код в повторно используемые и общедоступные компоненты — начните программировать лучше и быстрее.

Совместное использование повторно используемых компонентов кода в команде · Бит

Легко делитесь повторно используемыми компонентами между проектами и приложениями, чтобы работать быстрее в команде. Совместная разработка…

bit.dev

Движки JavaScript сегодня встроены во все современные браузеры. Когда файл JavaScript загружается в браузер, движок JavaScript будет выполнять каждую строку файла сверху вниз (для упрощения объяснения мы избегаем подъема в JS). Движок JavaScript будет анализировать код построчно, преобразовывать его в машинный код и затем выполнять. Некоторые из известных движков JS перечислены ниже:

Механизмы браузера

Механизм JavaScript состоит из двух компонентов:

  1. Стек контекста выполнения
  2. Куча
Механизм JavaScript

Стек контекста выполнения (ECS)

Стек контекста выполнения представляет собой структуру данных стека23, следующую за Last In 90 Принцип Out (LIFO) (последний элемент, попавший в стек, будет первым элементом, который будет удален из стека). ECS хранит контекст выполнения для каждой функции. Контекст выполнения определяется как объект, в котором хранятся локальные переменные, функции и объекты. Примитивные значения, такие как int , bool и т. д. хранятся внутри объекта контекста выполнения, в то время как определения функций и объекты не хранятся внутри объекта контекста выполнения, они хранятся внутри кучи. Объект контекста выполнения просто имеет ссылку или адрес памяти, где хранятся эти определения функций и объекты.

Механизм JavaScript всегда выполняет функцию, которая находится наверху стека контекста выполнения.

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

Когда выполнение функции завершено, JS-движок сам удаляет ее из ECS и начинает выполнение функции на вершине стека.

Поскольку движок JavaScript имеет только одну ECS, он может выполнять только одну вещь за раз, которая находится на вершине ECS. Это то, что делает JavaScript однопоточным.

Куча

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

Давайте разберемся с кучей и стеком контекста выполнения на примере. Рассмотрим код ниже:

Execution Context Stack exampleOutput: Стек контекста выполнения exampleGIF: Стек контекста выполнения на каждом этапе выполнения вышеуказанного кода приступить к его выполнению. Когда JS-движок достигает определения функции functionOne , он сохраняет определение функции в памяти кучи и ее ссылку в глобальном контексте выполнения. Когда functionOne вызывается движком JS, он помещает functionOne контекст выполнения внутрь ECS и начинает выполнение functionOne , приостанавливая выполнение глобального контекста выполнения.

Когда JS-движок вызывает functioninTwo внутри functionOne , JS-движок помещает functionTwo внутрь ECS и начинает выполнение functionTwo , приостанавливая выполнение functionOne . Как только весь код внутри functionTwo выполняется, JS-движок выводит контекст выполнения functionTwo и перезапускает выполнение оставшегося кода functionOne .

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

До сих пор мы обсуждали движок JavaScript, но движок JavaScript не работает изолированно. Он работает внутри среды, называемой средой выполнения JavaScript, вместе со многими другими компонентами. JRE отвечает за асинхронность JavaScript. Именно по этой причине JavaScript может добавлять прослушиватели событий и асинхронно выполнять HTTP-запросы.

JRE похожа на контейнер, который состоит из следующих компонентов:

  1. JS Engine
  2. Web API
  3. Очередь обратного вызова или очередь сообщений
  4. Таблица событий
  5. Цикл событий
Среда выполнения JavaScript

Мы уже узнали о движке JavaScript. Давайте перейдем к другим компонентам в списке:

Веб-API

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

Примеры:

  1. DOM API для управления DOM. document.getElementById , addEventListerner , document. querySelectorAll и т. д. являются частью API DOM, предоставляемого браузером, к которому мы можем получить доступ с помощью JavaScript.
  2. Вызовы AJAX или XMLHttpRequest. Поскольку веб-API зависят от браузера, а XMLHttpRequest — это веб-API, нам пришлось по-другому реализовать XMLHttpRequest для IE, прежде чем нас спас JQuery (помните?).
  3. Таймер работает как setTimeout и setInterval также предоставляются браузером.

Когда механизм JavaScript находит какой-либо метод веб-API, он отправляет этот метод в таблицу событий, где ожидает, пока не произойдет событие. В случае вызовов AJAX механизм JS будет отправлять вызовы AJAX в таблицу событий и продолжит выполнение кода после вызова Ajax. Вызов AJAX будет ожидать в таблице событий, пока не будет возвращен ответ от вызова AJAX. В случае функции таймера, например, setTimeout , он ждет, пока счетчик таймера не станет равным нулю.

Таблица событий

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

Очередь обратного вызова, или Очередь сообщений, или Очередь событий

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

Обратные вызовы веб-API перемещаются из таблицы событий в очередь событий при возникновении события. Например, когда вызовы AJAX завершаются и возвращается ответ, он перемещается из таблицы событий в очередь событий. Точно так же, когда время ожидания метода setTimout становится равным нулю, оно перемещается из очереди событий в таблицу событий.

Цикл событий

Методы не выполняются ни в таблице событий, ни в очереди событий. Они выполняются движком JavaScript, только если он присутствует в ECS. Итак, для выполнения любого метода нам нужно переместить этот метод из очереди обратного вызова в стек контекста выполнения. Это то, что делает цикл событий! Цикл событий постоянно проверяет, пуст ли стек контекста выполнения и есть ли какие-либо сообщения в очереди событий. Он переместит метод из очереди обратного вызова в ECS только тогда, когда стек контекста выполнения пуст.

Мы поймем все вышеперечисленные концепции с помощью функции setTimeout , поэтому давайте посмотрим, что такое setTimeout

setTimeout

setTimeout и функции setInterval называются в JavaScript as17r9016 setInterval. Они используются для планирования выполнения функций. Они не предоставляются самим движком JS, но они предоставляются браузером как часть объекта окна . Рассмотрим окно object

 console.log(window) 
Функции setTimeout и setInterval для объекта окна

Здесь мы видим, что обе функции таймера setTimeout и setInterval доступны для объекта window . Поскольку они являются функциями окна , значение этого внутри обеих функций таймера всегда будет равным окну .

setTimeout синтаксис функции

 setTimeout(callbackFunction, timeToDelay) 

setTimeout принимает функцию обратного вызова и время в миллисекундах в качестве параметра. callbackFunction выполняется после timeToDelay миллисекунд (этот оператор будет подробно обсуждаться позже, он несколько неверен)

Пример:

setTimeout базовый синтаксис

Здесь, несмотря на то, что printStatement функция должна выполняться через 100 миллисекунд, но Движок JS не будет ждать 100 миллисекунд, пока его выполнение не начнется, а выполнит следующую строку, т.е. console.log без задержки.

Следовательно, первым печатаемым оператором является «Я буду ожидать первым» , а не «Я буду напечатан через 100 миллисекунд» , хотя в соответствии с обычным потоком выполнения JS-движка, то есть сверху вниз, «Я будет напечатано через 100 миллисекунд» должен был быть напечатан первым.

Это показывает, что setTimeout не блокирует выполнение скрипта.

Рассмотрим еще один интересный пример

setTimeout выполнение через ноль миллисекунд

В приведенном выше примере функция printStatement должна быть выполнена через ноль миллисекунд. Мы ожидаем, что printStatement будет выполнено немедленно, но мы будем удивлены, увидев результат. «Все равно я буду казнен первым» будет напечатано первым, а затем «Я буду напечатан через 0 миллисекунд» будет напечатано.

В следующей части этой статьи, когда мы будем обсуждать внутреннее функционирование JavaScript и setTimeout function, you will able to understand both the above setTimeout function behaviour

Passing parameter to setTimeout callback function

Cancel setTimeout callback execution

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

Пример

clearTimeout для очистки таймера

метод clearTimeout также доступен для объекта окна точно так же, как setTimeout метод

Рассмотрим код ниже время ожидания 1000 мс

Стек контекста выполнения при вызове функции functionOne будет таким, как показано ниже:

GIF: setTimout в среде выполнения JavaScript. Куча не отображается в движке JS.

Давайте выполним приведенный выше код построчно

Строка 1 : Как только код загружается в браузере, JS-движок помещает глобальный контекст выполнения в стек контекста выполнения и начинает выполнение скрипта.

Строка 23 : Когда вызывается functionOne , JS-движок отправляет контекст выполнения functionOne в ECS и начинает выполнение functionOne

Строка 12 устанавливает , он перемещает setTimoutFunction вместе с метаданными, т.е. 1000 мс, в контейнер веб-API или таблицу событий. setTimeoutFunction останется в контейнере веб-API до 1000 мс. JS-движок не ждет 1000 мс для выполнения функции обратного вызова setTimeout , он продолжает выполнять код после setTimeout function

Строка 14 : JS-движки повторяют цикл for 10000000000 раз. Это занимает более 1000 мс. Между тем, в контейнере веб-API, когда 1000 мс завершает setTimeoutFunction перемещается из контейнера веб-API в очередь обратного вызова или очередь сообщений. JS-движок не знает об этих вещах, он не знает, где находится setTimeoutFunction .

В течение этого периода цикл событий непрерывно проверяет как стек контекста выполнения, так и цикл событий. Он проверяет две вещи:

  1. Если стек контекста выполнения пуст
  2. Есть ли какие-либо сообщения или события в очереди обратного вызова.

Если цикл событий обнаружит, что стек контекста выполнения пуст и в очереди обратного вызова есть сообщение, он переместит связанный метод из очереди обратного вызова в стек контекста выполнения. Как только этот метод будет перемещен в стек контекста выполнения, механизм JS начнет его выполнение. Если в очереди обратного вызова находится несколько сообщений, сообщения будут перемещаться в стек контекста выполнения одно за другим в том порядке, в котором они были добавлены в очередь (помните, что очередь обратного вызова — «первым пришел — первым обслужен»)

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

В приведенном выше примере, пока для цикла выполняется движком JS, в стеке есть два контекста выполнения — functionOne контекст выполнения и глобальный контекст выполнения. Следовательно, setTimeoutFunction , хотя и присутствует в очереди событий, не будет перемещен в стек контекста выполнения циклом обработки событий.

Строка 21 : Выполнение functionOne было завершено движком JS, поэтому контекст выполнения functionOne будет удален из ECS. Теперь в ECS у нас есть глобальный контекст выполнения. Даже если прошло 1000 миллисекунд, setTimeoutFunction все равно останется в очереди обратного вызова, поскольку ECS не пуст.

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

Строка 25 : После выполнения этой строки глобальный контекст выполнения будет удален из стека контекста выполнения.

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

Из приведенного выше примера мы можем понять следующее:

  1. JavaScript является однопоточным, т.е. в любой момент времени он может выполнять только один фрагмент кода.
  2. Движок JS выполняет функцию, которая находится на вершине стека
  3. Асинхронные задания, такие как ожидание setTimeout Функция обратного вызова для выполнения не выполняется самим движком JavaScript. Это выполняется средой выполнения JavaScript
  4. Веб-API перемещаются из контейнера веб-API в очередь обратного вызова только тогда, когда происходит требуемое событие. Например, функция обратного вызова setTimeout перемещается в очередь обратного вызова только по истечении времени, указанного в setTimeout .

Теперь рассмотрим наш предыдущий пример setTimeout с нулем миллисекунд

В приведенном выше примере, когда механизм JS находит функцию setTimeout в коде, он перемещает функцию обратного вызова printStatement в контейнер веб-API. Поскольку задержка составляет ноль (0) миллисекунд, printStatement немедленно перемещается в очередь обратного вызова. Но поскольку console.log("Все равно я буду выполнен первым") не выполняется, глобальный контекст выполнения все еще присутствует в стеке контекста выполнения. Следовательно, printStatement не будет перемещен в ECS. Как только console.log("Все равно я буду выполнен первым") выполняется, JS Engines удаляет глобальный контекст выполнения из ECS, после чего цикл событий переместит функцию printStatement в ECS, а затем «Я буду напечатан через 0 миллисекунд» будет напечатано .

Рассмотрим другой пример

setTimeout код для нулевого времени ожидания в миллисекундах

Вывод :

setTimeout с нулевым временем ожидания — выход

Здесь setTimeoutFunction будет выполняться только тогда, когда весь код из скрипта будет выполнен и ECS пуста

Другой setTimeout неожиданный пример вывода

To5 ниже

  • 0 Рассмотрим код ниже output, нам нужны две концепции JavaScript — замыкания и setTimeout .

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

    Следовательно, все функции в JavaScript имеют доступ ко всем свойствам и методам, определенным в глобальной области видимости.

    Давайте попробуем понять пример

    JS-движок будет повторять цикл for четыре раза, и каждый раз setTimeout функция обратного вызова будет помещена в очередь обратного вызова. Когда значение ai станет равным четырем, цикл for будет завершен с функцией обратного вызова 4 setTimeout в очереди обратного вызова. Как только ECS опустеет, все эти 4 функции setTimeout будут выполняться одна за другой. А как насчет значения

    аи ?

    Просто взгляните на объект window

     conosle.log(window) 
    переменная ai объекта windows

    Как показано на изображении выше, объект window имеет значение ai as 4. доступен ли ai за пределами цикла for ? Мы обсудим это в следующих статьях.