Скрипты и время их выполнения. SetTimeout и setInterval

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

Синтаксис такой:

Код
const timerId = setTimeout();

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

Код
const timerId = setTimeout(function() {
  console.log('Hello');
});

Вторым аргументом указывается время в миллисекундах, через которое должен произойти вызов этой функции.

Код
const timerId = setTimeout(function() {
  console.log('Hello');
}, 2000);

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

Код
const timerId = setTimeout(function(text) {
  console.log(text);
}, 2000, 'Hello'); // Hello

Как вариант, можно в качестве аргумента передавать функцию.

Например, создадим функцию logger и передадим её в качестве первого аргумента:

Код
const timerId = setTimeout(logger, 2000);  

function logger() {
  console.log('Hello');
}

Спустя две секунды функция logger вызовется и в консоли появится сообщение "Hello"

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

Очистить setTimeout можно с помощью команды clearInterval():

Код
const timerId = setTimeout(logger, 2000);  

clearInterval(timerId);

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

Есть такая структура кода:

Код
<button class="btn">Animation</button>
<div class="wrapper">
  <div class="box"></div>
</div>

И минимальный набор стилей:

Код
.box {
  position: absolute;
  width: 100px;
  height: 100px;
  background-color: blue;
}
.wrapper {
  position: relative;
  width: 400px;
  height: 400px;
  border: 3px solid red;
}
button {
  margin-bottom: 10px;
  margin-top: 10px;
  width: 100px;
  height: 40px;
  background-color: yellow;
  border: none;
}

Получим кнопку с классом .btn

Код
const btn = document.querySelector('.btn');

И добавляем для неё обработчик события, суть его - после клика на кнопку будет запускаться таймер:

Код
btn.addEventListener('click', () => {
  const timerId = setTimeout(logger, 2000);
})

function logger () {
  console.log('Text');
}

Если нам нужно, чтобы скрипт повторялся не один раз, а каждый раз через определённое количество времени, то для этого используется setInterval()

Код
btn.addEventListener('click', () => {
  const timerId = setInterval(logger, 2000);
})

Сейчас у нас такой код скрипта:

Код
btn.addEventListener('click', () => {
  const timerId = setInterval(logger, 2000);
})

clearInterval(timerId);

function logger () {
  console.log('Text');
}

И сброс таймера в нём не работает, потому что переменная timerId объявлена внутри функции, она локальная и не видна снаружи.

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

Код
let timerId;

btn.addEventListener('click', () => {
  timerId = setInterval(logger, 2000);
});

Но и теперь сброс таймера не будет работать, потому что он идёт в потоке синхронного кода, то есть, за счёт того, что функция setInterval бесконечно срабатывает и срабатывает через каждые 2 секунды, код до clearInterval() попросту не доходит.

Поэтому можно сделать так: внутри функции logger мы можем отслеживать, сколько раз повторился setInterval.

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

Как только значение i достигнет, к примеру, трёх, запускается очистка таймера.

Код
const btn = document.querySelector('.btn');
let timerId;
let i = 0;

btn.addEventListener('click', () => {
  timerId = setInterval(logger, 2000);
})

function logger() {
  if (i === 3) {
  clearInterval(timerId);
  }
  console.log('Text');
  i++;
}

Рекурсивный setTimeout. Чем он лучше setInterval?



Всё дело в том, что когда таймер setInterval работает, он не учитывает, как долго будет работать код функции внутри него, ему всё равно, сколько по времени будет работать функция logger.

И может получиться так, что, к примеру, таймер установлен на 0.5 секунды, а код функции выполняется 3 секунды. Как будет в этом случае работать таймер?

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

Для решения такой проблемы обычно используют рекурсивный setTimeOut, разберём на примере:

Код
let id = setTimeout(function log() {
  console.log('Hello'); // не важно сколько по времени идет этот код
  id = setTimeout(log, 500); // запустится через полсекунды после выполнения кода выше
}, 500);

Что происходит теперь?

Запускается основной таймер, проходит полсекунды, запускается код функции log, в консоль выводится сообщение 'Hello'.

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

Практика: построение анимации





Цель: при нажатии на кнопку, синий квадрат плавно по диагонали перемещается в нижний правый угол.

Начинаем писать функцию myAnimation. Внутри функции получим элемент, с которым будем взаимодействовать (синий квадрат).
Там же создадим переменную, в которую запишем нулевое значение стартовой позиции квадрата.

Код
function myAnimation() {
  const elem = document.querySelector('.box');
  let pos = 0;
};

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

Код
function myAnimation() {
  const elem = document.querySelector('.box');
  let pos = 0;

  function frame() {
      if (pos == 300) {
        clearInterval()
      } else {
        pos++;
       elem.style.top = pos + 'px';
       elem.style.left = pos + 'px';
      }
  }
};

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

Код
function myAnimation() {
  const elem = document.querySelector('.box');
  let pos = 0;

  const id = setInterval(frame, 10); // вызов функции каждые 10 миллисекунд

  function frame() {
      if (pos == 300) {
        clearInterval(id)
      } else {
        pos++;
       elem.style.top = pos + 'px';
       elem.style.left = pos + 'px';
      }
  }
};

Осталось в конце кода добавить обработчик события для кнопки:

Код
btn.addEventListener('click', myAnimation);


JavaScript-анимацииИсходники урока

Всего комментариев: 1

Имя *:
Email *:
Код *:
Хостинг от uCoz