Понимание setTimeout(). setTimeout() может привести к неожиданным результатам… | by Piyush Kochhar

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

setTimeout()

Это функция в JavaScript, которая задерживает выполнение кода.

Части setTimeout():

Части setTimeout()

SetTimeout состоит из 3 частей:

  • Обратный вызов
  • Операторы, которые должны выполняться внутри обратного вызова
  • Время в миллисекундах (время задержки)

Приведенный выше код печатает «Привет» и «Пока» в терминале через 1 секунду (1000 мс = 1 с).

Кажется, это просто! Но если мы не знаем, как setTimeout() ведет себя под капотом, мы можем получить неожиданное поведение.

Пример 1 : Single setTimeout()
  Вывод  :Sum = 3 
1 2

Объяснение : В приведенном выше примере setTimeout() имеет задержку 1 секунду. Таким образом, мы получаем сначала сумму, а значения переменных «а» и «б» через 1 секунду.

Пример 2: Блокировка и неблокировка

  Вывод  : Мы изучаем setTimeout 
10
20
30
Операторы снаружи блокируются
Через 1 секунду
3

2 Операторы внутри не блокируются 7 Объяснение

: setTimeout() - это неблокирующий , что означает, что он будет запущен, когда будут выполнены операторы вне его, а затем через одну секунду он будет выполнен. Все остальные операторы, не являющиеся частью setTimeout(), равны 9.0027 блокирует , что означает, что никакой другой оператор не будет выполняться до завершения текущего оператора. На рисунке ниже показаны блокирующие и неблокирующие операторы.

Блокирующие и неблокирующие операторы

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

Стек вызовов

Обрабатывает все операторы блокировки и операторы, поступающие из очереди событий.

Цикл событий

Обрабатывает все задержанные операторы, например. setTimeout(), setInterval(), события, сетевые вызовы, промисы и все остальные асинхронные операции. Когда таймер setTimeout достигает 0, операторы выходят из цикла событий и переходят в очередь событий.

Очередь событий

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

Стек вызовов, цикл событий и очередь событий

Итак, давайте посмотрим, как выполняется пример 2.

Шаг 1 : Мы разделяем блокирующие и неблокирующие операторы.

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

  Вывод  : <пусто> 

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

  Выход  : Мы изучаем setTimeout 
10
20
30
Внешние операторы блокируются

Шаг 3 : Операторы выходят из очереди событий и отправляются в стек вызовов только тогда, когда весь код блокировки в стеке вызовов выполнено, а стек вызовов пуст. Затем выполняются операторы внутри setTimeout(), и, следовательно, мы получаем наш вывод.

  Выход : Мы изучаем setTimeout 
10
20
30
Внешние операторы блокируются
Через 1 секунду
3
Операторы внутри не блокируют

Теперь у нас есть некоторое представление о том, как работает setTimeout(). Давайте рассмотрим еще несколько примеров.

Пример 3: Несколько setTimeout()

  Вывод  :a = 10 
b = 20
c = 30
Через 0 секунд
Через 1 секунду
Через 2 секунды
Через 3 секунды

2: Объяснение 2 выше пример легкий.

Помните, что все операторы блокировки выполняются первыми, пока все setTimeouts находятся в ожидании цикла событий. Затем каждый переходит в очередь событий один за другим, когда, поскольку 0s имеет самый низкий таймер, он выполняется первым, затем выполняется 1s setTimeout(), затем выполняется 2s setTimeout() и, наконец, 3s setTimeout() выполняется.

Выглядит это примерно так:

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

Пример 4: Вложенный setTimeout()

  Вывод:  ноль 
шесть
один
два
пять
три
четыре

и «шесть» в качестве нашего вывода в начале, а внешний setTimeout ждет 1 с в цикле событий, затем он переходит в очередь событий, а затем выходит и выполняется, поэтому теперь печатаются «один» и «два». Но мы видим, что «пять» тоже напечатано. Почему? Потому что, как только компилятор JS считывает внутренний setTimeout() (который является неблокирующим оператором) и отправляет его в цикл обработки событий, где он ждет 2 секунды. И пока внутренний setTimeout() ожидает, внешний setTimeout() выполняет все операторы блокировки внутри себя. Следовательно, печатается «пять». А затем, когда внутренний setTimeout() дождался, он попадает в очередь событий и выполняется, так мы получаем «три» и «четыре» в качестве вывода.

На рисунках ниже показано, как работает приведенный выше код:

Пример 5 : Странное поведение 0 и 1 мс.
  Вывод: 
один
ноль

Объяснение : Наш вывод должен был быть «ноль» и «единица», потому что 0 мс меньше 1 мс. Но вместо этого это «единица» и «ноль». Причина в том, что 1 мс — это такой короткий промежуток времени, который в основном эквивалентен 0 мс, поэтому он сразу же переходит в очередь событий из цикла событий и сначала выполняется через стек вызовов.

Пример 6 : Переполнение тайм-аута
  Вывод:  TimeoutOverflowWarning: 2147483648 не помещается в 32-разрядное целое число со знаком.  
Продолжительность тайм-аута была установлена ​​равной 1.
один
ноль

Объяснение : Любое значение времени выше 2³⁰ не помещается в 32-битное целое число со знаком. Итак, компилятор выдает TimeoutOverflowWarning и автоматически устанавливает значение времени равным 1. И из предыдущего примера, поскольку 1 мс в основном эквивалентно 0 мс. Сначала он выполняется, а затем выполняется 0 мс setTimeout().

Пример 7: setTimeout и циклы

  Вывод:  3 
3
3

Объяснение : Цикл for является блокирующим оператором, поэтому setTimeout() не является блокирующим. Цикл создает 3 setTimeouts, которые переходят в цикл событий, а затем в очередь событий. Пока все setTimeouts ожидают в очереди событий, значение «i» уже изменилось на 3 в стеке вызовов. И setTimeouts печатает текущее значение «i».

Одним из решений вышеуказанной проблемы является использование let вместо var, что создает блочную область.

  Вывод:  0 
1
2

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

  Результат:  0 
1
2

Спасибо, что являетесь частью нашего сообщества! Подпишитесь на наш канал YouTube или присоединитесь к курсу собеседования Skilled.dev по программированию .

Вопросы для собеседования по программированию | Skilled.dev

Курс по прохождению собеседования по программированию

skill.dev

Создание функции для настройки setTimeout

Преподаватель: [0:00] Когда вы создаете setTimeout, вы можете передать функцию и время, которое проходит до запуска этой функции. В теле функции вы говорите, что вы хотите, чтобы произошло. Мы выполним выход из консоли. Как только я нажму «Сохранить», через одну секунду вы увидите, что в консоли все готово.

[0:22] Чтобы получить больше контроля над этим, мы рассмотрим элементы, которые вы можете извлечь и контролировать. Скажем, например, время, вызываемая функция и даже тот факт, что setTimeout вызывается немедленно, — это то, что мы можем контролировать.

[0:36] Мы делаем это, помещая все это внутрь функции. Мы создадим функцию с именем createTimeout. Это начнется как просто функция. Внутри тела вы можете взять тайм-аут и вставить его туда. Когда я нажму «Сохранить», вы не увидите, что здесь сделано, потому что мы не вызывали функцию.

[0:57] Нам нужно вызвать createTimeout, а затем нажать «Сохранить». Затем, через одну секунду, вы увидите, что здесь всё готово. Первая часть, которую я хочу извлечь, это одна секунда здесь. Я вырежу это. Я назову это временем. Я перенесу это в наши споры, звоните на этот раз. Теперь мы можем настроить здесь, сколько времени займет тайм-аут. Я нажму сохранить.

[1:16] Теперь это будет выполняться через одну секунду. Если я сделаю это гораздо меньшим значением, например, 100, я нажму «Сохранить», и он запустится за 10-ю долю секунды. Теперь мы вернулись к той же проблеме, с которой мы столкнулись в начале: как только мы вызываем эту функцию, она запускается немедленно.

[1:31] Решение, как и раньше, заключается в том, чтобы обернуть все это внутри функции. Мы можем сделать это, поместив стрелочную функцию прямо здесь. Теперь, когда я нажимаю «Сохранить», ничего не запускается, потому что теперь это возвращает функцию. Нам нужно что-то присвоить, например, позволить тайм-ауту равно, а затем вызвать это. Мы вызовем тайм-аут.

[1:53] Теперь, когда я нажимаю «Сохранить», вы видите, что через десятую долю секунды появляется надпись «Готово». Давайте удалим это. Я собираюсь переименовать это в одну секунду и изменить это обратно на целую секунду. Вы можете видеть, что если я продублирую это несколько раз, я смогу создать функцию, поддерживающую две секунды, и другую, поддерживающую три секунды.

[2:12] Я называю это двумя секундами. Назовите это три секунды. Теперь я могу вызвать одну секунду, две секунды и три секунды.