[ Web Proxy ]
URL:
Viewing: https://uz.javascript.info/settimeout-setinterval [Back]  [Original]

Rejalashtirish: setTimeout va setInterval
UZ

Ushbu ochiq manbali loyihani butun dunyo bo'ylab odamlar uchun taqdim etmoqchimiz.

Ushbu qo'llanmaning mazmuni sizning tilingizga tarjima qilishga yordam bering!

    Javascript.info saytidan qidirish:
    O'quv qo'llanmasidan qidiring:
    Light themeDark theme
    DanskEnglishEspaolFranaisIndonesiaItalianoTrkeOzbek

    Rejalashtirish: setTimeout va setInterval

    Biz funktsiyani hozir emas, balki malum bir vaqtdan keyin bajarishga qaror qilishimiz mumkin. Bu chaqiruvni rejalashtirish deb nomlanadi.

    Buning ikkita usuli mavjud:

    • setTimeout vaqt oraligidan keyin bir marta funktsiyani bajarishga imkon beradi.
    • setInterval funktsiyalarni muntazam ravishda ishlash oraligida 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 qollab-quvvatlanadi.

    setTimeout

    Sintaksis:

    let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)

    Parametrlar:

    func|code
    Funksiya yoki bajariladigan kod matni. Odatda, bu funktsiya. Tarixiy sabablarga kora kod matnni otkazish mumkin, ammo bu tavsiya etilmaydi.
    delay
    Ishlashdan oldin kechikish, millisekundlarda (1000 ms = 1 soniya), sukut boyicha 0.
    arg1, arg2
    Funktsiya uchun argumentlar (IE9- da qollab-quvvatlanmaydi)

    Masalan, ushbu kod bir soniyadan song 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 bolsa, unda JavaScript undan funktsiya yaratadi.

    Shunday qilib, bu ham ishlaydi:

    setTimeout("alert('Salom')", 1000);

    Ammo matnlardan foydalanish tavsiya etilmaydi, ularning orniga funktsiyalardan foydalaning, masalan:

    setTimeout(() => alert('Salom'), 1000);
    Funktsiyani otkazing, lekin uni ishlatmang

    Ajam dasturchilar bazida funktsiyadan keyin () qavslarini qoshib 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 ozgartirdik). Natijada, hech narsa bolmaydi:

    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 korinib turibdiki, brauzerda taymer identifikatori raqam hisoblanadi. Boshqa muhitda bu boshqa narsa bolishi mumkin. Masalan, Node.js taymer moslamasini qoshimcha usullar bilan qaytaradi.

    Shunga qaramay, ushbu usullar uchun universal spetsifikatsiya mavjud emas, shuning uchun hammasi yaxshi.

    Brauzerlar uchun taymerlar HTML5 standartidagi taymerlar bolimida tasvirlangan.

    setInterval

    setInterval usuli setTimeout bilan bir xil sintaksisga ega:

    let timerId = setInterval(func|code, [delay], [arg1], [arg2], ...)

    Barcha argumentlar bir xil manoga ega. setTimeout dan farqli olaroq, u funktsiyani faqat bir marta emas, balki berilgan vaqt oraligidan keyin muntazam ravishda ishlaydi.

    Boshqa chaqiruvlarni toxtatish uchun biz clearInterval(timerId) ni chaqirishimiz kerak.

    Quyidagi misol xabarni har 2 soniyada korsatadi. 5 soniyadan song chiqish toxtatiladi:

    // 2 soniya oralig'ida takrorlang
    let timerId = setInterval(() => alert('tick'), 2000);
    
    // 5 soniyadan keyin to'xtaydi
    setTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000);
    Chrome/Opera/Safari-da modal oynalar vaqtni muzlatib qoyadi

    IE va Firefox brauzerlarida ichki taymer alert/confirm/prompt korsatishda yurishni ni davom ettiradi, ammo Chrome, Opera va Safari-da ichki taymer muzlatilgan boladi.

    Shunday qilib, agar siz yuqoridagi kodni ishlatsangiz va alert oynasini bir muncha vaqt ochirmasangiz, unda Firefox/IE-da keyingi alert darhol bajariladi (oldingi chaqiruvdan 2 soniya song) va Chrome/Opera/Safari yana 2 soniyadan song (taymer alert vaqtida toxtaydi).

    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 malumot sorab sorov yuboradigan xizmatni yozishimiz kerak, ammo server haddan tashqari yuklangan bolsa, 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 bolsa, unda biz bajarilish vaqtini olchay 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 bolishi va 100ms dan oshishi mumkin.

    Bunday holda, interpretator func tugashini kutadi, songra rejalashtiruvchini tekshiradi va agar vaqt tugagan bolsa, uni darhol qayta ishlatadi.

    Chekka holatda, agar funktsiya har doim kechikish ms dan koproq vaqtni bajaradigan bolsa, 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.

    Axlat yigish

    Funktsiya setInterval/setTimeout ga otkazilganda, unga ichki havolalar yaratiladi va rejalashtiruvchida saqlanadi. Bu funktsiyani axlat yigishdan saqlaydi, hatto unga boshqa havolalar bolmasa ham.

    // rejalashtiruvchi chaqirguncha funktsiya xotirada qoladi
    setTimeout(function() {...}, 100);

    setInterval uchun funktsiya clearInterval chaqirilguncha xotirada qoladi.

    Yon tasiri bor. Funktsiya tashqi leksik muhitga murojaat qiladi, shuning uchun u yashab turib, tashqi ozgaruvchanlar ham yashaydi. Ular funktsiyadan kora koproq xotirani olishlari mumkin. Shunday qilib, biz endi rejalashtirilgan funktsiyaga muhtoj bolmaganda, juda kichik bolsa 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 bolib "Salom", keyin esa "Dunyo" boladi.

    CPU vazifalarni ajratish

    setTimeout dan foydalanib, CPU och vazifalarni ajratish uchun hiyla-nayrang bor.

    Masalan, sintaksisni takidlaydigan skript (ushbu sahifadagi kod misollarini ranglash uchun ishlatiladi) juda ogir protsessorga ega. Kodni ajratib korsatish uchun u tahlilni amalga oshiradi, koplab rangli elementlarni yaratadi, ularni hujjatga qoshadi kop 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, songra setTimeout(..., 0) va boshqalarni ishlatib yana 100 satrni rejalashtiring.

    Aniqlik uchun korib 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 bolsangiz, sahifadagi boshqa tugmachalarni bosishga harakat qilsangiz aslida JavaScript toxtatib qoyganligini korasiz, 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 korsatilishi mumkin (lekin umid qilamanki bunday bolmaydi, chunki ularning soni unchalik katta emas).

    Ichki ornatilgan setTimeout yordamida ishni ikkiga bolaylik:

    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 toliq ishlaydi.

    Biz ishning bir qismini bajaramiz (*):

    1. Birinchi bajarilish: i=1...1000000.
    2. Ikkinchi bajarilish: i=1000001..2000000.
    3. va shnday, while, i ning 1000000 ga teng bolinishini tekshiradi.

    Keyinchalik, agar biz hali tugamagan bolsak, 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 bolmasdan tezligi bilan taqqoslanadi. Umumiy hisoblash vaqtida katta farq yoq.

    Ularni yaqinlashtirish uchun keling, ozgarish kiritamiz.

    Biz rejalashtirishni count() boshiga otkazamiz:

    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.

    Brauzer ichidagi ichki taymerlarning minimal kechikishi

    Brauzerda ichki ornatilgan taymerlarning ishlash tezligi cheklangan. HTML5 standarti: beshta ichki taymerdan song, interval kamida tort millisoniyaga teng bolishga majbur boladi.

    Keling, bu nimani anglatishini quyidagi misol bilan namoyish etamiz. Undagi setTimeout chaqiruvi 0ms dan keyin ozini qayta rejalashtiradi. Har bir chaqiruv oldingi vaqtdan haqiqiy vaqtni times satrida eslab qoladi. Haqiqiy kechikishlar qanday korinishga ega? Koraylikchi:

    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 boladi va biz 9, 15, 20, 24... koramiz.

    Ushbu cheklov qadimgi davrlardan kelib chiqqan va koplab skriptlar unga tayanadi, shuning uchun u tarixiy sabablarga kora 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 korsatishga ruxsat berish

    Brauzer ichidagi skriptlarning yana bir foydasi shundaki, ular foydalanuvchi uchun rivojlanish satrini yoki biror narsani korsatishi mumkin. Buning sababi shundaki, brauzer odatda skript tugagandan song barcha boyash ishlarini bajaradi.

    Shunday qilib, agar biz bitta katta funktsiyani bajaradigan bolsak, u biror narsani ozgartirgan bolsa ham, ozgarishlar 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 ozgartirishlar butun hisoblash tugagandan song paydo boladi.

    Agar biz uni qismlarga ajratish uchun setTimeout dan foydalansak, unda ozgarishlar oraligida qollaniladi, shuning uchun bu yaxshiroq korinadi:

    <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 korsatadi.

    Xulosa

    • setInterval(func, delay, ... args) va setTimeout(func, delay, ... args) usullari func kechikish milisoniyalardan keyin muntazam/bir marta bajarishga imkon beradi.
    • Amalga oshirishni bekor qilish uchun setInterval/setTimeout tomonidan qaytarilgan qiymat bilan clearInterval/clearTimeout ni chaqirishimiz kerak.
    • Ichki setTimeout chaqiruvlari setInterval ga moslashuvchan alternativa. Shuningdek, ular qatl etish oraligidagi minimal vaqtni kafolatlashlari mumkin.
    • setTimeout(..., 0) nol-timeout rejalashtirish chaqiruvni iloji boricha tezroq, lekin joriy kod tugagandan song rejalashtirish uchun ishlatiladi.

    Bazilar setTimeout(..., 0) holatlaridan foydalanadilar:

    • Skript osilib qolmasligi uchun protsessorga och vazifalarni qismlarga bolish.
    • Jarayon davom etayotganida brauzerga boshqa narsani qilishga ruxsat berish (bajarilish satrini boyash).

    Iltimos, barcha rejalashtirish usullari aniq kechiktirishga kafolat bermasligini unutmang. Biz rejalashtirilgan kodda bunga ishonmasligimiz kerak.

    Masalan, brauzer ichidagi taymer juda kop sabablarga kora sekinlashishi mumkin:

    • CPU haddan tashqari yuklangan.
    • Brauzer yorligi fon rejimida.
    • Noutbuk batareyada.

    Brauzer va sozlamalarga qarab, taymerning minimal taymer olchamlarini (minimal kechikish) 300 yoki hatto 1000 soniyaga oshirishi mumkin.

    Vazifalar

    Har soniyada sonni from dan boshlab va to bilan tugaydigan sonlarni chiqaradigan printNumbers(from, to) funktsiyasini yozing.

    Natijaning ikkita variantini tuzing.

    1. setInterval dan foydalanish.
    2. Rekursiv setTimeout dan foydalanish.
    yechim

    setInterval dan foydalanish:

    function printNumbers(from, to) {
      let current = from;
    
      let timerId = setInterval(function () {
        alert(current);
        if (current == to) {
          clearInterval(timerId);
        }
        current++;
      }, 1000);
    }
    
    // usage:
    printNumbers(5, 10);

    setTimeout dan foydalanish:

    function printNumbers(from, to) {
      let current = from;
    
      setTimeout(function go() {
        alert(current);
        if (current < to) {
          setTimeout(go, 1000);
        }
        current++;
      }, 1000);
    }
    
    // foydalanish:
    printNumbers(5, 10);

    Shuni esda tutingki, ikkala echimda ham birinchi chiqishdan oldin dastlabki kechikish mavjud. Bazan darhol birinchi chiqishni amalga oshirish uchun satr qoshishimiz kerak, buni bajarish oson.

    Quyidagi kodda setTimeout chaqiruvi rejalashtirilgan, keyin 100 ms dan koproq vaqtni talab qiladigan ogir hisoblash amalga oshiriladi.

    Rejalashtirilgan funktsiya qachon ishlaydi?

    1. Tsikldan keyin.
    2. Tsikldan oldin.
    3. Tsiklning boshida.

    alert nimani korsatmoqda?

    let i = 0;
    
    setTimeout(() => alert(i), 100); // ?
    
    // ushbu funktsiyani bajarish vaqti> 100ms deb taxmin qiling
    for (let j = 0; j < 100000000; j++) {
      i++;
    }
    yechim

    Har qanday setTimeout faqat joriy kod tugagandan song ishlaydi.

    i oxirgi boladi: 100000000.

    let i = 0;
    
    setTimeout(() => alert(i), 100); // 100000000
    
    // ushbu funktsiyani bajarish vaqti> 100ms deb taxmin qiling
    for (let j = 0; j < 100000000; j++) {
      i++;
    }
    O'quv qo'llanma xaritasi

    Izohlar

    izoh berishdan oldin buni o'qing
    • Agar sizda nimani yaxshilash kerakligi haqida takliflaringiz bo'lsa - iltimos, GitHub muammosini yuboring yoki izoh berish o'rniga so'rov yuboring.
    • Agar siz maqolada biror narsani tushunolmasangiz - iltimos, batafsilroq ma'lumot bering.
    • Bir nechta so'z so'zlarini kiritish uchun <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)

    Web Proxy Viewer  |  New URL  |  Original Page