Таймери

Таймер - це затримка виконання функції на вказаний час.

JavaScript підтримує роботу з двома типами таймерів - одноразовим setTimeout() та багаторазовим setInterval().

Кожна з цих функцій приймає в себе щонайменше 2 аргумента: функцію, яка має виконатися, і час в мілісекундах:

setTimeout(function(){
  console.log('hello!');
}, 5000);

let i = 5;
setInterval(fun, 1000);
function fun(){ console.log(i++); }

Якщо у функцію треба передати аргументи - вони прописуються в кінець, після часу затримки:

setTimeout(function(a, b, c){
  console.log(a + b + c);
}, 3000, 5, 10, 20);

setInterval(fun, 1000, 'hello');
function fun(k){ console.log(k); }

Таймер можна зупинити, вимкнути. Кожна з вищевказаних функцій повертає ідентифікатор таймера (число), зупинити можна двома функціями:

var a = setTimeout(fun, 5000);
var b = setInterval(fun, 8000);
button.onclick = function(){
  clearTimeout(a);
  clearInterval(b);
}

Функції таймерів є асинхронними, тобто - вони можуть виконуватися паралельно з виконанням іншого коду. Про це треба пам'ятати і бути обережним.

Розглянемо приклади:

// що відобразить alert?
let x = 5;
setTimeout(function(){ alert(x); }, 100);
x = 10;

// хочемо вивести в консоль текст перших 5-и абзаців:
let p = document.querySelectorAll('p');
for (var i = 0; i < 5; i++){
  setTimeout(function(){
    console.log(p[i].innerText);
  }, (i + 1) * 1000);
}

(Наступна інформація була актуальна у 2015-2017 роках, наразі є застарілою через повсюдне використання оператора let).

Ця задача популярна на співбесідах:
"Що буде виведено в консоль?"

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
  setTimeout(function() {
    console.log('Index: ' + i + ', element: ' + arr[i]);
  }, 3000);
}

Останній варіант має декілька варіантів правильного розв'язку:

// використаємо let замість var (подробиці тут):
let p = document.querySelectorAll('p');
for (let i = 0; i < 5; i++){
  setTimeout(function(){ console.log(p[i].innerText); }, (i + 1) * 1000);
}

// передамо у функцію потрібний абзац у якості аргумента:
var p = document.querySelectorAll('p');
for (var i = 0; i < 5; i++){
  setTimeout(function(e){ console.log(e.innerText); }, (i + 1) * 1000, p[i]);
}

// елемент можна передати в функцію не як аргумент, а як батька:
...
setTimeout(function(){ console.log(this.innerText); }.bind(p[i]), ...);
...

// можна використати метод forEach, з ним також код буде працювати вірно. Спробуйте самі.

Завдання

  1. Опишіть порядок виконання команд у наступних алгоритмах: const arr = [10, 12, 15, 21];

    for (var i = 0; i < arr.length; i++) {
      setTimeout((function(i) {
        return function() { console.log(i, arr[i]); }
      })(i), 3000);
    }

    for (var i = 0; i < arr.length; i++) {
      (function(i){
        setTimeout(function() { console.log(i, arr[i]); }, 3000);
      })(i);
    }

Домашнє завдання

  1. Через 5 секунд після відкриття сторінки вивести на екран повідомлення "5 seconds!".
  2. Зробити таймер, що починає відлік з 3:30, рахує посекундно у зворотньому напрямку і після досягнення значення 0:00 замість цифр видає текст "BOOM!!!" (прим.: тероризм - це погано).
  3. Виправте код, щоб у консолі видавало цифри 1, 2, 3, 4, 5: for (var i = 0; i < 5; i++) {
      setTimeout(function() {
        console.log(i + 1);
      }, i * 1000);
    }
    Переробіть цей код так, щоб у ньому була рекурсія.
    Спробуйте застосувати setInterval.
  4. Зробити годинник, що відображає в браузері поточні дату та час - день, місяць, рік, день тижня, години, хвилини, секунди. Синхронізацію проводити 1 раз на 5 хвилин.
    Рекомендації до виконання: раціонально створити масив змінних [Y, M, D, d, h, m, s] та 3 функції: синхронізацію з годинником комп’ютера; функцію, що повертає оформлену строку з датою та часом та функцію, яка буде робити розрахунки додавання секунди та перевірки хвилин, годин і т.п. на перевищення допустимих значень. В таймері setInterval потрібно зробити додавання секунди, виведення строки та 1 раз на 300 циклів проводити синхронізацію.
    Зауваження. Можна щосекунди брати системний час та виводити його вбудованими функціями, типу .toString(), .toLocaleString() і т.п., але перед нами стоїть задача навчитися оперувати складовими дати та часу.