Понимание таймеров в JavaScript. Callback-функции, setTimeout, setInterval и requestAnimationFrame

Stas Bagretsov
Sep 24, 2018 · 12 min read

Что такое callback-функции?

Зачем нам вообще колбэки?

function first(){
console.log(1);
}
function second(){
console.log(2);
}
first();
second();
// 1
// 2
function first(){
// Симулируем задержку кода
setTimeout( function(){
console.log(1);
}, 500 );
}
function second(){
console.log(2);
}
first();
second();
first();
second();
// 2
// 1

Создаём callback

function doHomework(subject) {
alert( Starting my ${subject} homework. );
}
doHomework('math');
// Alerts: Starting my math homework.
function doHomework(subject, callback) {
alert( Starting my ${subject} homework. );
callback();
}
doHomework('math', function() {
alert('Finished my homework');
});
function doHomework(subject, callback) {
alert( Starting my ${subject} homework. );
callback();
}
function alertFinished(){
alert('Finished my homework');
}
doHomework('math', alertFinished);

Пример из реального мира

T.get('search/tweets', params, function(err, data, response) {
if(!err){
// This is where the magic will happen
} else {
console.log(err);
}
})

Таймеры setTimeout и setInterval

setTimeout()

setTimeout ( expression, timeout );
<input type="button" name="sayHello" value="Wait for my Hello!"onclick="setTimeout('alert(\'Hello!\')', 4000)"/>
function sayHello() {alert('Hello');}setTimeout(sayHello, 1000);
function sayHello(message, person) {alert( message + ', '+ person );}setTimeout(sayHello, 1000, "Hi", "Monica"); // Hi, Monica
setTimeout("alert('Hello')", 1000);
setTimeout(() => alert('Hello'), 1000);

setInterval()

setInterval ( expression, interval );
// Hello показывается каждые 3 секунды
let timerId= setInterval(() => alert('Hello'), 3000);
// Повторения прекращаются после 6 секунд с id таймера.
setTimeout(() => { clearInterval(timerId); alert('Bye'); }, 6000);

requestAnimationFrame()

Почему нужно использовать requestAnimationFrame?

Оптимизация браузером

Анимации работают, когда их видно

Меньшее потребление питания

Используем requestAnimationFrame

// Анимируем
function animate(highResTimestamp) {
requestAnimationFrame(animate);
// Анимируем что-нибудь…
}
// Запускаем анимацию.
requestAnimationFrame(animate);
var requestID = requestAnimationFrame(animate);

Отменяем кадры анимации

cancelAnimationFrame(requestID);

Полифил

Подготавливаем HTML и CSS

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>requestAnimationFrame Demo</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="page-wrapper">
<h1>requestAnimationFrame Demo</h1>
<div class="controls">
<button type="button" id="startBtn">Start Animation</button>
<button type="button" id="stopBtn">Stop Animation</button>
<button type="button" id="resetBtn">Reset</button>
</div>
<canvas id="stage" width="640" height="100"></canvas>
</div>
<script src="raf-polyfill.js"></script>
<script src="script.js"></script>
</body>
</html>

Настраиваем JavaScript

(function() {// Get the buttons.
var startBtn = document.getElementById('startBtn');
var stopBtn = document.getElementById('stopBtn');
var resetBtn = document.getElementById('resetBtn');
// Остальной код будет тут…
}());
// Canvasvar canvas = document.getElementById('stage');// 2d контекст отрисовки.var ctx = canvas.getContext('2d');// Стиль наполнения для контекста отрисовки.ctx.fillStyle = '#212121';// Переменная в которой будет храниться requestID.var requestID;// Переменные для отрисовки позиций и объекта.var posX = 0;var boxWidth = 50;var pixelsPerFrame = 5; // Количество пикселей, на которое должен двинуться блок в каждом кадре.// Отрисовка изначального блока в canvas.ctx.fillRect(posX, 0, boxWidth, canvas.height);

Написание анимированной функции

// Animate.function animate() {requestID = requestAnimationFrame(animate);// Проверка если блок не достиг конца отрисовки в канвасе.// В противном случае завершается анимация.if (posX <= (canvas.width — boxWidth)) {ctx.clearRect((posX — pixelsPerFrame), 0, boxWidth, canvas.height);ctx.fillRect(posX, 0, boxWidth, canvas.height);posX += pixelsPerFrame;} else {cancelAnimationFrame(requestID);}}

Цепляем кнопки

// EventListener для кнопки старта.startBtn.addEventListener(‘click’, function(e) {e.preventDefault();// Запуск анимации.requestID = requestAnimationFrame(animate);});// EventListener для кнопки стоп.stopBtn.addEventListener(‘click’, function(e) {e.preventDefault();// завершаем анимацию;cancelAnimationFrame(requestID);});// EventListener для кнопки сброса.resetBtn.addEventListener(‘click’, function(e) {e.preventDefault();// Сбрасываем X позицию на ноль.posX = 0;// Очищаем canvas.ctx.clearRect(0, 0, canvas.width, canvas.height);// Отрисовываем изначальный блок.ctx.fillRect(posX, 0, boxWidth, canvas.height);});

Stas Bagretsov

Written by

Надеюсь верую вовеки не придет ко мне позорное благоразумие. webdev/sports/books

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade