Таймерҳо ва асинхронӣ
Ин барои чӣ лозим аст
То ҳол барномаҳо қатъиян аз боло ба поён иҷро мешуданд: сатр паси сатр, бе таваққуф. Аммо барномаҳои воқеӣ доим бояд интизор шаванд: баъд аз як сония маслиҳат нишон диҳанд, ҳар сония соатро нав кунанд, ҷавоби серверро интизор шаванд, ба клик ҷавоб диҳанд. Кодеро, ки фавран не, балки баъдтар иҷро мешавад, асинхронӣ меноманд.
Таймерҳо — соддатарин коди асинхронӣ. Дар онҳо қоидаи асосии JavaScript хуб намоён аст: коди
мавқуфгузошта ҳеҷ гоҳ коди ҷориро қатъ намекунад, балки навбати худро интизор мешавад. Бе фаҳмидани
ин қоида на промисҳоро, на async/await-ро ва на он ки React саҳифаро кай нав мекунад, фаҳмидан мумкин нест.
setTimeout — як бор баъдтар иҷро кардан
setTimeout(() => {
console.log("Як сония гузашт");
}, 1000);
console.log("Таймер гузошта шуд");
Натиҷа:
Таймер гузошта шуд
Як сония гузашт
- Аргументи аввал — функсия (callback), ки бояд даъват шавад.
- Дуюм — таъхир бо миллисонияҳо: 1000 мс = 1 сония.
setTimeoutинтизор намешавад: вай «баъд аз як сония даъват кун»-ро қайд мекунад ва фавран идоракуниро бармегардонад. Барои ҳамин «Таймер гузошта шуд» аввал чоп мешавад.
Ба callback маълумотро тавассути closure додан мумкин аст:
function remind(text, ms) {
setTimeout(() => console.log("Ёдрасӣ: " + text), ms);
}
remind("об нӯшидан", 200);
remind("варзиш кардан", 100);
Натиҷа:
Ёдрасӣ: варзиш кардан
Ёдрасӣ: об нӯшидан
Онҳо аз рӯи тартиби вақт кор мекунанд, на аз рӯи тартиби даъват.
Бекор кардан: clearTimeout
setTimeout рақами таймер-ро бармегардонад. Бо он таймерро то кор карданаш бекор кардан мумкин аст:
const id = setTimeout(() => console.log("Бум!"), 500);
clearTimeout(id);
console.log("Таймер бекор шуд");
Натиҷа:
Таймер бекор шуд
«Бум!» ҳеҷ гоҳ чоп намешавад.
setInterval — такрор кардан
setInterval(callback, мс) функсияро бо таваққуфи мс боз ва боз даъват мекунад, то вақте ки онро
бо clearInterval қатъ накунанд:
let count = 0;
const id = setInterval(() => {
count++;
console.log("Тик " + count);
if (count === 3) {
clearInterval(id);
console.log("Ист");
}
}, 100);
Натиҷа:
Тик 1
Тик 2
Тик 3
Ист
clearInterval-и фаромӯшшуда — хатои маъмул: интервал то абад кор мекунад.
Чаро коди мавқуфгузошта интизор мешавад: давраи рӯйдодҳо
JavaScript кодро дар як ҷараён (thread) иҷро мекунад: дар ҳар лаҳза танҳо як функсия кор мекунад. То он тамом нашавад, чизи дигар оғоз намеёбад. Ин чунин сохта шудааст:
- Аввал тамоми коди оддӣ (синхронӣ) иҷро мешавад — аз боло ба поён.
- Callback-ҳои барои иҷро тайёр (таймери кор кардашуда, клик, ҷавоби сервер) ба навбат меистанд.
- Вақте коди ҷорӣ тамом шуд, давраи рӯйдодҳо (event loop) callback-и навбатиро аз навбат гирифта, пурра иҷро мекунад. Баъд навбатиро — ва ҳамин тавр тамоми вақти кори барнома.
Барои ҳамин ҳатто таймер бо таъхири 0 фавран кор намекунад:
console.log("1");
setTimeout(() => console.log("2"), 0);
console.log("3");
Натиҷа:
1
3
2
setTimeout(…, 0) маънои «ҳарчи зудтар, вале баъд аз коди ҷорӣ»-ро дорад. Таъхир бошад — вақти
ҳадди ақали интизорӣ аст: агар барнома бо давраи дароз банд бошад, таймер то озод шудани он интизор мешавад.
const start = Date.now();
setTimeout(() => console.log("Таймер дер монд: " + (Date.now() - start >= 100)), 10);
while (Date.now() - start < 100) {
// давраи банд барои 100 мс — навбат интизор аст
}
console.log("Давра тамом шуд");
Натиҷа:
Давра тамом шуд
Таймер дер монд: true
Таймер баъд аз 10 мс тайёр буд, вале танҳо баъд аз он иҷро шуд, ки давра ҷараёнро озод кард. Барои ҳамин дар браузер ҳисобҳои вазнин саҳифаро «ях мекунанд»: кликҳо ҳам дар навбат интизор мешаванд.
Callback-ҳо ва «дӯзахи callback-ҳо»
Функсияи асинхронӣ натиҷаро бо return баргардонда наметавонад — натиҷа ҳоло нест. Барои ҳамин аз вай
хоҳиш мекунанд: «вақте тайёр шуд, ин функсияро даъват кун»:
function loadUser(id, callback) {
setTimeout(() => callback({ id, name: "Аня" }), 50); // гӯё ҷавоби сервер
}
loadUser(1, (user) => console.log("Бор шуд: " + user.name));
console.log("Бор мекунем...");
Натиҷа:
Бор мекунем...
Бор шуд: Аня
Агар қадамҳо чандто бошанд ва ҳар кадом қадами пешинро интизор шавад, callback-ҳо ба дохили ҳамдигар ҷой мегиранд:
loadUser(1, (user) => {
loadOrders(user, (orders) => {
loadDetails(orders[0], (details) => {
// ...ва боз чуқуртар
});
});
});
Ин «зинапоя»-ро дӯзахи callback-ҳо (callback hell) меноманд: хонданаш душвор аст ва хатоҳоро дар ҳар сатҳ коркард кардан лозим меояд. Ин мушкилро промисҳо ҳал мекунанд — мавзӯи навбатӣ.
Debounce: интизор шудан, то корбар ист кунад
Усули муфид бар асоси таймерҳо. Корбар дар ҷустуҷӯ менависад ва барои ҳар ҳарф ба сервер дархост фиристодан исроф аст. Беҳтар аст интизор шавем, то вай навиштанро бас кунад:
function debounce(fn, ms) {
let timer; // дар closure байни даъватҳо зинда аст
return (...args) => {
clearTimeout(timer); // даъвати нав таймери кӯҳнаро бекор мекунад
timer = setTimeout(() => fn(...args), ms);
};
}
const search = debounce((q) => console.log("Ҷустуҷӯ: " + q), 100);
search("j");
search("ja");
search("jav");
Натиҷа:
Ҷустуҷӯ: jav
Се даъвати зуд — як дархост, бо қимати охирин.
Дар дохил чӣ гуна кор мекунад
Таймерҳоро худи JavaScript не, балки муҳит ҳисоб мекунад: браузер ё Node.js. Вақте setTimeout даъват
мешавад, муҳит функсия ва вақтро дар хотир нигоҳ медорад. Вақте вақт мерасад, вай callback-ро ба навбати
вазифаҳо мегузорад. Муҳаррики JavaScript коди синхрониро то охир иҷро мекунад ва баъд давраи рӯйдодҳо
вазифаҳоро аз навбат якто-якто мегирад.
Аз ин се хулоса бармеояд:
- callback-и таймер ҳеҷ гоҳ дар мобайни функсияи дигар иҷро намешавад;
- таъхир — ин «на пештар аз», на «маҳз баъд аз»;
- то callback-ҳо набошанд, барнома танҳо интизор мешавад. Дар Node.js барнома вақте тамом мешавад, ки навбат холӣ аст ва ягон таймер намондааст.
Хатоҳои маъмул
- Даъват ба ҷойи додан.
setTimeout(sayHi(), 1000)sayHi-ро фавран даъват мекунад ва ба таймер натиҷаи онро медиҳад.setTimeout(sayHi, 1000)ёsetTimeout(() => sayHi(), 1000)лозим аст. - Натиҷаро бо
returnинтизор шудан. Қимати дар дохили callback-и таймер гирифташударо аз функсияи берунӣ баргардондан мумкин нест: вай аллакай тамом шудааст. Callback (ё промис) лозим аст. clearInterval-ро фаромӯш карданд. Интервал то қатъ кардан кор мекунад.- Сонияҳо ба ҷойи миллисонияҳо.
setTimeout(fn, 5)— ин 5 мс аст, на 5 сония. - Гумон кардан, ки
0— «фавран» аст. Callback танҳо баъд аз тамоми коди ҷорӣ иҷро мешавад.
Ёддошт
| Чӣ | Чӣ тавр |
|---|---|
| Як бор баъдтар иҷро кардан | const id = setTimeout(fn, ms) |
| Бекор кардан | clearTimeout(id) |
| Такрор кардан | const id = setInterval(fn, ms) |
| Такрорро қатъ кардан | clearInterval(id) |
| Баъд аз коди ҷорӣ | setTimeout(fn, 0) |
| Тартиб | аввал тамоми коди синхронӣ, баъд навбат |
| Debounce | clearTimeout(timer); timer = setTimeout(...) |