Biz funktsiyani hozir emas, balki maâlum bir vaqtdan keyin bajarishga qaror qilishimiz mumkin. Bu âchaqiruvni rejalashtirishâ deb nomlanadi.
Buning ikkita usuli mavjud:
setTimeoutvaqt oraligâidan keyin bir marta funktsiyani bajarishga imkon beradi.setIntervalfunktsiyalarni muntazam ravishda ishlash oraligâida ishlashga imkon beradi.
Ushbu usullar JavaScript spetsifikatsiyasining bir qismi emas. Ammo aksariyat muhitlar ichki rejalashtiruvchiga ega va ushbu usullarni taqdim etadi. Xususan, ular barcha brauzerlarda va Node.js qoâllab-quvvatlanadi.
setTimeout
Sintaksis:
let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)
Parametrlar:
func|code- Funksiya yoki bajariladigan kod matni. Odatda, bu funktsiya. Tarixiy sabablarga koâra kod matnni oâtkazish mumkin, ammo bu tavsiya etilmaydi.
delay- Ishlashdan oldin kechikish, millisekundlarda (1000 ms = 1 soniya), sukut boâyicha 0.
arg1,arg2â¦- Funktsiya uchun argumentlar (IE9- da qoâllab-quvvatlanmaydi)
Masalan, ushbu kod bir soniyadan soâng sayHi() ni chaqiradi:
function sayHi() {
alert('Salom');
}
setTimeout(sayHi, 1000);
Argumentlar bilan:
function sayHi(phrase, who) {
alert( phrase + ', ' + who );
}
setTimeout(sayHi, 1000, "Salom", "John"); // Salom, John
Agar birinchi argument matn boâlsa, unda JavaScript undan funktsiya yaratadi.
Shunday qilib, bu ham ishlaydi:
setTimeout("alert('Salom')", 1000);
Ammo matnlardan foydalanish tavsiya etilmaydi, ularning oârniga funktsiyalardan foydalaning, masalan:
setTimeout(() => alert('Salom'), 1000);
Ajam dasturchilar baâzida funktsiyadan keyin () qavslarini qoâshib xato qilishadi:
// noto'g'ri!
setTimeout(sayHi(), 1000);
Bu ishlamaydi, chunki setTimeout funktsiyaga havolani kutadi. Va bu yerda sayHi() funktsiyasi ishlaydi va bajarilish natijasi setTimeout ga uzatiladi. Bizning holatimizda sayHi() natijasi undefined (funktsiya hech narsa bermaydi), shuning uchun hech narsa rejalashtirilmagan.
clearTimeout yordamida bekor qilish
setTimeout ga chaqiruv bajarishni bekor qilish uchun ishlatadigan âtaymer identifikatoriâ timerId ni qaytaradi.
Bekor qilish uchun sintaksis:
let timerId = setTimeout(...);
clearTimeout(timerId);
Quyidagi kodda biz funktsiyani rejalashtiramiz va keyin uni bekor qilamiz (fikrimizni oâzgartirdik). Natijada, hech narsa boâlmaydi:
let timerId = setTimeout(() => alert("never happens"), 1000);
alert(timerId); // taymer identifikatori
clearTimeout(timerId);
alert(timerId); // bir xil identifikator (bekor qilinganidan keyin null bo'lmaydi)
alert natijasidan koârinib turibdiki, brauzerda taymer identifikatori raqam hisoblanadi. Boshqa muhitda bu boshqa narsa boâlishi mumkin. Masalan, Node.js taymer moslamasini qoâshimcha usullar bilan qaytaradi.
Shunga qaramay, ushbu usullar uchun universal spetsifikatsiya mavjud emas, shuning uchun hammasi yaxshi.
Brauzerlar uchun taymerlar HTML5 standartidagi taymerlar boâlimida tasvirlangan.
setInterval
setInterval usuli setTimeout bilan bir xil sintaksisga ega:
let timerId = setInterval(func|code, [delay], [arg1], [arg2], ...)
Barcha argumentlar bir xil maânoga ega. setTimeout dan farqli oâlaroq, u funktsiyani faqat bir marta emas, balki berilgan vaqt oraligâidan keyin muntazam ravishda ishlaydi.
Boshqa chaqiruvlarni toâxtatish uchun biz clearInterval(timerId) ni chaqirishimiz kerak.
Quyidagi misol xabarni har 2 soniyada koârsatadi. 5 soniyadan soâng chiqish toâxtatiladi:
// 2 soniya oralig'ida takrorlang
let timerId = setInterval(() => alert('tick'), 2000);
// 5 soniyadan keyin to'xtaydi
setTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000);
IE va Firefox brauzerlarida ichki taymer alert/confirm/prompt koârsatishda âyurishniâ ni davom ettiradi, ammo Chrome, Opera va Safari-da ichki taymer âmuzlatilganâ boâladi.
Shunday qilib, agar siz yuqoridagi kodni ishlatsangiz va alert oynasini bir muncha vaqt oâchirmasangiz, unda Firefox/IE-da keyingi alert darhol bajariladi (oldingi chaqiruvdan 2 soniya soâng) va Chrome/Opera/Safari â yana 2 soniyadan soâng (taymer alert vaqtida toâxtaydi).
Rekursiv setTimeout
Biror narsani muntazam ravishda bajarishning ikkita usuli mavjud.
Ulardan biri setInterval. Ikkinchisi quyidagicha rekursiv setTimeout:
/** ning o'rniga:
let timerId = setInterval(() => alert('tick'), 2000);
*/
let timerId = setTimeout(function tick() {
alert('tick');
timerId = setTimeout(tick, 2000); // (*)
}, 2000);
Yuqoridagi setTimeout navbatdagi chaqiruvni joriy chaqiruv oxirida (*) rejalashtiradi.
Rekursiv setTimeout â setInterval ga qaraganda ancha moslashuvchan usul. Shu tarzda, keyingi chaqiruv joriy chaqiruv natijalariga qarab boshqacha tarzda rejalashtirilishi mumkin.
Masalan, biz serverga har 5 soniyada maâlumot soârab soârov yuboradigan xizmatni yozishimiz kerak, ammo server haddan tashqari yuklangan boâlsa, u intervalni 10, 20, 40 soniyagacha oshirishi kerak â¦
Mana, psevdokod:
let delay = 5000;
let timerId = setTimeout(function request() {
...so'rov yuboring...
if (server haddan tashqari yuklanganligi sababli so'rov bajarilmadi) {
// keyingi ishga tushirish oralig'ini oshiring
delay *= 2;
}
timerId = setTimeout(request, delay);
}, delay);
Agar bizda doimiy ravishda protsessorga chalingan vazifalar boâlsa, unda biz bajarilish vaqtini oâlchay olamiz va ertami-kechmi navbatdagi chaqiruvni rejalashtiramiz.
Rekursiv setTimeout qatllar orasidagi kechikishni kafolatlaydi, setInterval esa bunday qilmaydi.
Keling, ikkita kod qismini taqqoslaylik. Birinchisi setInterval dan foydalanadi:
let i = 1;
setInterval(function() {
func(i++);
}, 100);
Ikkinchisi rekursiv setTimeout dan foydalanadi:
let i = 1;
setTimeout(function run() {
func(i++);
setTimeout(run, 100);
}, 100);
setInterval uchun ichki rejalashtiruvchi har 100 msda func(i) ishlatadi:
Siz sezdingizmi?
setInterval uchun func chaqiruvlari orasidagi haqiqiy kechikish koddan kamroq!
Bu odatiy holdir, chunki func bajarilishi vaqti oraliqning bir qismini âsarflaydiâ.
Ehtimol, func ning bajarilishi biz kutganimizdan uzoqroq boâlishi va 100ms dan oshishi mumkin.
Bunday holda, interpretator func tugashini kutadi, soângra rejalashtiruvchini tekshiradi va agar vaqt tugagan boâlsa, uni darhol qayta ishlatadi.
Chekka holatda, agar funktsiya har doim kechikish ms dan koâproq vaqtni bajaradigan boâlsa, u holda chaqiruvlar umuman pauza qilinmasdan amalga oshiriladi.
Va bu yerda setTimeout rekursivi uchun rasm:
Rekursiv setTimeout belgilangan kechikishni kafolatlaydi (bu yerda 100ms).
Buning sababi, avvalgi chaqiruv oxirida yangi chaqiruv rejalashtirilgan.
Funktsiya setInterval/setTimeout ga oâtkazilganda, unga ichki havolalar yaratiladi va rejalashtiruvchida saqlanadi. Bu funktsiyani axlat yigâishdan saqlaydi, hatto unga boshqa havolalar boâlmasa ham.
// rejalashtiruvchi chaqirguncha funktsiya xotirada qoladi
setTimeout(function() {...}, 100);
setInterval uchun funktsiya clearInterval chaqirilguncha xotirada qoladi.
Yon taâsiri bor. Funktsiya tashqi leksik muhitga murojaat qiladi, shuning uchun u yashab turib, tashqi oâzgaruvchanlar ham yashaydi. Ular funktsiyadan koâra koâproq xotirani olishlari mumkin. Shunday qilib, biz endi rejalashtirilgan funktsiyaga muhtoj boâlmaganda, juda kichik boâlsa ham, uni bekor qilish yaxshiroqdir.
Zero delay setTimeout
Maxsus foydalanish holati mavjud: setTimeout(func, 0) yoki shunchaki setTimeout(func).
Bu func ning bajarilishini eng qisqa vaqt ichida rejalashtiradi. Ammo rejalashtiruvchi uni joriy kod tugagandan keyingina chaqiradi.
Shunday qilib, funktsiya joriy kodi âdarholâ ishlashni rejalashtirmoqda. Boshqacha qilib aytganda, asenkron ravishda.
Masalan, âSalomâ, keyin darhol âDunyoâ chiqadi:
setTimeout(() => alert("Dunyo"));
alert("Salom");
Birinchi satr âchaqiruvni 0ms dan keyin kalendarga kiritadiâ. Ammo rejalashtiruvchi joriy kod tugagandan keyingina âkalendari tekshiradiâ, shuning uchun birinchi boâlib "Salom", keyin esa "Dunyo" boâladi.
CPU vazifalarni ajratish
setTimeout dan foydalanib, CPU och vazifalarni ajratish uchun hiyla-nayrang bor.
Masalan, sintaksisni taâkidlaydigan skript (ushbu sahifadagi kod misollarini ranglash uchun ishlatiladi) juda ogâir protsessorga ega. Kodni ajratib koârsatish uchun u tahlilni amalga oshiradi, koâplab rangli elementlarni yaratadi, ularni hujjatga qoâshadi â koâp narsalarni talab qiladigan katta matn uchun. Hatto brauzer âosilib qolishiâ mumkin, bu esa juda yomon.
Shunday qilib, biz uzun matnni qismlarga ajratishimiz mumkin. Dastlab 100 satr, soângra setTimeout(..., 0) va boshqalarni ishlatib yana 100 satrni rejalashtiring.
Aniqlik uchun koârib chiqish uchun oddiyroq misol keltiramiz. Bizda 1 dan 1000000000 gacha hisoblash funktsiyasi mavjud.
Agar siz uni ishlatsangiz, CPU osilib qoladi. Server tomonidagi JS uchun bu aniq seziladi va agar siz uni brauzerda ishlatayotgan boâlsangiz, sahifadagi boshqa tugmachalarni bosishga harakat qilsangiz â aslida JavaScript toâxtatib qoâyganligini koârasiz, u tugamaguncha boshqa harakatlar ishlamaydi.
let i = 0;
let start = Date.now();
function count() {
// og'ir ish qilish
for (let j = 0; j < 1e9; j++) {
i++;
}
alert("Done in " + (Date.now() - start) + 'ms');
}
count();
Brauzerda hatto âskript juda uzoq davom etadiâ degan ogohlantirish koârsatilishi mumkin (lekin umid qilamanki bunday boâlmaydi, chunki ularning soni unchalik katta emas).
Ichki oârnatilgan setTimeout yordamida ishni ikkiga boâlaylik:
let i = 0;
let start = Date.now();
function count() {
// og'ir ishning bir qismini bajaring (*)
do {
i++;
} while (i % 1e6 != 0);
if (i == 1e9) {
alert("Done in " + (Date.now() - start) + 'ms');
} else {
setTimeout(count); // yangi chaqiruvni rejalashtirish (**)
}
}
count();
Endi brauzer interfeysi âhisoblashâ jarayonida toâliq ishlaydi.
Biz ishning bir qismini bajaramiz (*):
- Birinchi bajarilish:
i=1...1000000. - Ikkinchi bajarilish:
i=1000001..2000000. - â¦va shnday,
while,ining1000000ga teng boâlinishini tekshiradi.
Keyinchalik, agar biz hali tugamagan boâlsak, keyingi chaqiruv (**) da rejalashtirilgan.
count ijrolari orasidagi pauzalar JavaScript-ni interpretatoriga boshqa biron bir narsani qilishi va boshqa foydalanuvchi harakatlariga munosabat bildirishi uchun yetarli darajada ânafasâ beradi.
Diqqatga sazovor tomoni shundaki, ikkala variant ham ishni setTimeout ga ajratish bilan va boâlmasdan â tezligi bilan taqqoslanadi. Umumiy hisoblash vaqtida katta farq yoâq.
Ularni yaqinlashtirish uchun keling, oâzgarish kiritamiz.
Biz rejalashtirishni count() boshiga oâtkazamiz:
let i = 0;
let start = Date.now();
function count() {
// rejalashtirishni boshiga siljiting
if (i < 1e9 - 1e6) {
setTimeout(count); // yangi chaqiruvni rejalashtirish
}
do {
i++;
} while (i % 1e6 != 0);
if (i == 1e9) {
alert("Done in " + (Date.now() - start) + 'ms');
}
}
count();
Endi biz count() ni boshlaganimizda va yana count() kerakligini bilsak, ishni darhol bajarishdan oldin rejalashtiramiz.
Agar siz uni ishlatsangiz, unda sezilarli darajada kam vaqt talab etilishini sezasiz.
Brauzerda ichki oârnatilgan taymerlarning ishlash tezligi cheklangan. HTML5 standarti: âbeshta ichki taymerdan soâng, interval kamida toârt millisoniyaga teng boâlishga majbur boâladi.â
Keling, bu nimani anglatishini quyidagi misol bilan namoyish etamiz. Undagi setTimeout chaqiruvi 0ms dan keyin oâzini qayta rejalashtiradi. Har bir chaqiruv oldingi vaqtdan haqiqiy vaqtni times satrida eslab qoladi. Haqiqiy kechikishlar qanday koârinishga ega? Koâraylikchi:
let start = Date.now();
let times = [];
setTimeout(function run() {
times.push(Date.now() - start); // oldingi chaqiruvdan kechikishni eslash
if (start + 100 < Date.now()) alert(times); // 100ms dan keyin kechikishlarni ko'rsating
else setTimeout(run); // else re-schedule
});
// chiqish namunasi:
// 1,1,1,1,9,15,20,24,30,35,40,45,50,55,59,64,70,75,80,85,90,95,100
Birinchi taymerlar darhol ishlaydi (xuddi shu xususiyatda yozilganidek), keyin kechikish paydo boâladi va biz 9, 15, 20, 24... koâramiz.
Ushbu cheklov qadimgi davrlardan kelib chiqqan va koâplab skriptlar unga tayanadi, shuning uchun u tarixiy sabablarga koâra mavjud.
Server tomonidagi JavaScript-da bu cheklov mavjud emas va zudlik bilan mos kelmaydigan ishni rejalashtirishning boshqa usullari mavjud, masalan process.nextTick va setImmediate Node.js uchun. Shunday qilib, tushuncha faqat brauzerga xosdir.
Brauzerga koârsatishga ruxsat berish
Brauzer ichidagi skriptlarning yana bir foydasi shundaki, ular foydalanuvchi uchun rivojlanish satrini yoki biror narsani koârsatishi mumkin. Buning sababi shundaki, brauzer odatda skript tugagandan soâng barcha âboâyashâ ishlarini bajaradi.
Shunday qilib, agar biz bitta katta funktsiyani bajaradigan boâlsak, u biror narsani oâzgartirgan boâlsa ham, oâzgarishlar tugamaguncha hujjatda aks etmaydi.
Mana demo:
<div id="progress"></div>
<script>
let i = 0;
function count() {
for (let j = 0; j < 1e6; j++) {
i++;
// joriy i ni <div> ga qo'ying
// (biz bu yerda aniq bo'lishi kerak, ichki HTML haqida ko'proq maxsus bobda gaplashamiz)
progress.innerHTML = i;
}
}
count();
</script>
Agar siz uni ishlatsangiz, i ga oâzgartirishlar butun hisoblash tugagandan soâng paydo boâladi.
Agar biz uni qismlarga ajratish uchun setTimeout dan foydalansak, unda oâzgarishlar oraligâida qoâllaniladi, shuning uchun bu yaxshiroq koârinadi:
<div id="progress"></div>
<script>
let i = 0;
function count() {
// og'ir ishning bir qismini bajaring (*)
do {
i++;
progress.innerHTML = i;
} while (i % 1e3 != 0);
if (i < 1e9) {
setTimeout(count);
}
}
count();
</script>
Endi <div> i ning ortib borayotgan qiymatlarini koârsatadi.
Xulosa
setInterval(func, delay, ... args)vasetTimeout(func, delay, ... args)usullarifunckechikish milisoniyalardan keyin muntazam/bir marta bajarishga imkon beradi.- Amalga oshirishni bekor qilish uchun
setInterval/setTimeouttomonidan qaytarilgan qiymat bilanclearInterval/clearTimeoutni chaqirishimiz kerak. - Ichki
setTimeoutchaqiruvlari âsetIntervalga moslashuvchan alternativa. Shuningdek, ular qatl etish oraligâidagi minimal vaqtni kafolatlashlari mumkin. setTimeout(..., 0)nol-timeout rejalashtirish chaqiruvni âiloji boricha tezroq, lekin joriy kod tugagandan soângâ rejalashtirish uchun ishlatiladi.
Baâzilar setTimeout(..., 0) holatlaridan foydalanadilar:
- Skript âosilib qolmasligiâ uchun protsessorga och vazifalarni qismlarga boâlish.
- Jarayon davom etayotganida brauzerga boshqa narsani qilishga ruxsat berish (bajarilish satrini boâyash).
Iltimos, barcha rejalashtirish usullari aniq kechiktirishga kafolat bermasligini unutmang. Biz rejalashtirilgan kodda bunga ishonmasligimiz kerak.
Masalan, brauzer ichidagi taymer juda koâp sabablarga koâra sekinlashishi mumkin:
- CPU haddan tashqari yuklangan.
- Brauzer yorligâi fon rejimida.
- Noutbuk batareyada.
Brauzer va sozlamalarga qarab, taymerning minimal taymer oâlchamlarini (minimal kechikish) 300 yoki hatto 1000 soniyaga oshirishi mumkin.
Izohlar
<code>yorlig'ini ishlating, bir nechta satrlar uchun - ularni<pre>yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepenâ¦)