Laravel Türkiye Discord Kanalı Forumda kod paylaşılırken dikkat edilmesi gerekenler!Birlikte proje geliştirmek ister misiniz?
  • Genel
  • Javascipt timeout, interval senkron çalışmama problemi

Merhaba,

Timeout yada interval ikisinide senkron olarak çalıştıramıyorum.

Örneğin şu senkron çalışmıyor, konsolda önce "bitti" yazıyor

let time = 5
const timer = await setInterval(() => {

console.log(time)
    --time
    if (time < 1) {
        clearInterval(timer)
    }

}, 1000)

console.log('bitti')

Promise ile denedim yine asenkron çalışıyor ve "bitti" önce yazıyor

const prom = new Promise((resolve, reject) => {
    let time = 5
    const timer = setInterval(() => {

        console.log(time)
        --time
        if (time < 1) {
            resolve(true)
        }

    }, 1000)
});

prom.then(() => {

    console.log('bitti')

})

callback olarak denedim yine asenkron çalışıyor, örneğin şurada önce bitti (async, await eklesemde birşey değişmiyor)

run(5, function () {

    console.log('bitti')

})

function run(time, call) {
    const timer = setInterval(() => {
    
        console.log(time)
        --time
        if (time < 1) {
            clearInterval(timer)
        }
    
    }, 1000)

    call()

}

Nerede yanlış yapıyorum ?

Teşekkürler

  • mgsmus bunu yanıtladı.
  • DreamerBird Javascript single-threaded yani tek iş parçacıklı bir dil olduğu için tek seferde tek bir işlem yapabilir. O yüzden işler birbirini bloklar, biri bitmeden diğeri çalışmaz. Öte yandan yorumlanan bir dildir, yani derlenmez. Yorumlama işini ise Javascript motoru (engine) yapar. En çok bilinen ve kullanılan Javascript motoru ise Chrome V8'dir ama biz Javascript ile uğraşırken bu motor ile ilgili bir işlem yapmayız. Javascript motoru bir ortamda (environment) yer alır ve biz ortam ile haşır neşir oluruz. Bu ortamlar web API, kütüphane, tarayıcı vs olmuş oluyor. İşte bu noktada şöyle bir ayrım var, evet Javascript single-threaded bir dildir ama çalışma ortamı (runtime environment) öyle değildir. Çalışma ortamında iş parçacığı havuzu vardır ve işler havuza alınınca sync ya da aysnc programlama yapılabilir. Bunun sağlayan, hangi işin hangi sırada çalışacağını belirleyen ise çalışma ortamının sahip olduğu olay döngüsü mekanizmasıdır (event loop). Genel olarak Javascript'te kod iki şekilde çalışıyor; birincisi direkt, yani mesela console.log yazınca hemen çalışması gibi, diğeri ise callback şeklinde yani bir olaydan sonra ateşlenecek şekilde. Bu olay döngüsü mekanizması callback kısmını halleden mekanizma.

    Şu işlemlere bakarsanız:

    console.log("A")
    setTimeout(()=>console.log("B"), 1)
    console.log("C")
    A
    C
    B

    Javascript single-threaded dedik, bloklar dedik ama A B C şeklinde çıktı almamız gerekiyorken A C B şeklinde çıktı aldık. B işleminin C işlemini bloklaması gerekiyordu fakat olmadı. Bunun sebebi işte setTimeout işlemi burada, olay döngüsü mekanizması tarafından, çalışma ortamında gerçekleşen bir işlem oluyor. 3 işlem de sırayla çalışıyor aslında, birbirlerini blokladılar, ama setTimeout olay döngüsü mekanizması kuyruğuna bir iş parçacığı ekliyor, olay döngüsü mekanizması da sırası geldiğinde iş parçacığının çalışmasını sağlıyor. Sadece kendi içlerinde birbirlerini bloklayabilen iki iş parçacığı kuyruğu oluşmuş oluyor. Çalışma sırası doğru aslında, sizin istediğiniz sıra değil sadece çünkü siz bu durumda iki kuyruğun karışımından bir sıralama istiyorsunuz, bu mümkün mü? Evet ama 3. bir kuyruk ile.

    const timer = new Promise((resolve, reject) => {
      const i = setInterval(() => {
        clearInterval(i);
        resolve();
      }, 3000);
    });
    
    timer.then(() => {
      console.log('A');
    })

    3 saniye sonra A yazar fakat

    timer.then(() => {
      console.log('A');
    })
    
    console.log('B');

    yaparsanız önce B yazar, sonra 3 saniye sonra A yazar. B yazan önce belleğe alınmadı ama, önce A işlemi alındı sonra B. Birbirlerini blokladılar, bloklanamayan ise olay döngüsü mekanizması kuyruğunda işlenen callback. O farklı bir kuyrukta işlendi çünkü. Aynı kuyruktaki iş parçacıkları birbirini bloklayabilir.

    function sleep() {
      return new Promise((resolve, reject) => {
          const i = setInterval(() => {
            clearInterval(i);
            resolve();
          }, 3000);
      });
    }
    
    function loggingThread(content) {
        console.log(content)
    } 
    
    // Burası sizin sıralı iş parçacığı kuyruğunuz yani 3. kuyruk:
    async function threadQueue() {
        loggingThread('A'); // 1. iş parçacığı
        await sleep();
        loggingThread('B'); // 2. iş parçacığı
        await sleep();
        loggingThread('C'); // 3. iş parçacığı
    }
    
    threadQueue();
    A
    B
    C

    şeklinde yapmanız gerekiyor.

    Bu durumda "Nerede yanlış yapıyorum ?" sorunuzun cevabı iş parçacığı kuyruklarını karıştırıyorsunuz olmuş oluyor. Blocking, seri bir yapı için yapmanız gereken kendi iş parçacığı kuyruğunuzu oluşturmak, üstteki async function threadQueue() gibi. Böylece akışı kontrol altında tutabilirsiniz.

    https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
    https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide/In_depth
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
    https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals

    DreamerBird Javascript single-threaded yani tek iş parçacıklı bir dil olduğu için tek seferde tek bir işlem yapabilir. O yüzden işler birbirini bloklar, biri bitmeden diğeri çalışmaz. Öte yandan yorumlanan bir dildir, yani derlenmez. Yorumlama işini ise Javascript motoru (engine) yapar. En çok bilinen ve kullanılan Javascript motoru ise Chrome V8'dir ama biz Javascript ile uğraşırken bu motor ile ilgili bir işlem yapmayız. Javascript motoru bir ortamda (environment) yer alır ve biz ortam ile haşır neşir oluruz. Bu ortamlar web API, kütüphane, tarayıcı vs olmuş oluyor. İşte bu noktada şöyle bir ayrım var, evet Javascript single-threaded bir dildir ama çalışma ortamı (runtime environment) öyle değildir. Çalışma ortamında iş parçacığı havuzu vardır ve işler havuza alınınca sync ya da aysnc programlama yapılabilir. Bunun sağlayan, hangi işin hangi sırada çalışacağını belirleyen ise çalışma ortamının sahip olduğu olay döngüsü mekanizmasıdır (event loop). Genel olarak Javascript'te kod iki şekilde çalışıyor; birincisi direkt, yani mesela console.log yazınca hemen çalışması gibi, diğeri ise callback şeklinde yani bir olaydan sonra ateşlenecek şekilde. Bu olay döngüsü mekanizması callback kısmını halleden mekanizma.

    Şu işlemlere bakarsanız:

    console.log("A")
    setTimeout(()=>console.log("B"), 1)
    console.log("C")
    A
    C
    B

    Javascript single-threaded dedik, bloklar dedik ama A B C şeklinde çıktı almamız gerekiyorken A C B şeklinde çıktı aldık. B işleminin C işlemini bloklaması gerekiyordu fakat olmadı. Bunun sebebi işte setTimeout işlemi burada, olay döngüsü mekanizması tarafından, çalışma ortamında gerçekleşen bir işlem oluyor. 3 işlem de sırayla çalışıyor aslında, birbirlerini blokladılar, ama setTimeout olay döngüsü mekanizması kuyruğuna bir iş parçacığı ekliyor, olay döngüsü mekanizması da sırası geldiğinde iş parçacığının çalışmasını sağlıyor. Sadece kendi içlerinde birbirlerini bloklayabilen iki iş parçacığı kuyruğu oluşmuş oluyor. Çalışma sırası doğru aslında, sizin istediğiniz sıra değil sadece çünkü siz bu durumda iki kuyruğun karışımından bir sıralama istiyorsunuz, bu mümkün mü? Evet ama 3. bir kuyruk ile.

    const timer = new Promise((resolve, reject) => {
      const i = setInterval(() => {
        clearInterval(i);
        resolve();
      }, 3000);
    });
    
    timer.then(() => {
      console.log('A');
    })

    3 saniye sonra A yazar fakat

    timer.then(() => {
      console.log('A');
    })
    
    console.log('B');

    yaparsanız önce B yazar, sonra 3 saniye sonra A yazar. B yazan önce belleğe alınmadı ama, önce A işlemi alındı sonra B. Birbirlerini blokladılar, bloklanamayan ise olay döngüsü mekanizması kuyruğunda işlenen callback. O farklı bir kuyrukta işlendi çünkü. Aynı kuyruktaki iş parçacıkları birbirini bloklayabilir.

    function sleep() {
      return new Promise((resolve, reject) => {
          const i = setInterval(() => {
            clearInterval(i);
            resolve();
          }, 3000);
      });
    }
    
    function loggingThread(content) {
        console.log(content)
    } 
    
    // Burası sizin sıralı iş parçacığı kuyruğunuz yani 3. kuyruk:
    async function threadQueue() {
        loggingThread('A'); // 1. iş parçacığı
        await sleep();
        loggingThread('B'); // 2. iş parçacığı
        await sleep();
        loggingThread('C'); // 3. iş parçacığı
    }
    
    threadQueue();
    A
    B
    C

    şeklinde yapmanız gerekiyor.

    Bu durumda "Nerede yanlış yapıyorum ?" sorunuzun cevabı iş parçacığı kuyruklarını karıştırıyorsunuz olmuş oluyor. Blocking, seri bir yapı için yapmanız gereken kendi iş parçacığı kuyruğunuzu oluşturmak, üstteki async function threadQueue() gibi. Böylece akışı kontrol altında tutabilirsiniz.

    https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
    https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide/In_depth
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
    https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals

    Hocam çok teşekkürler verdiğiniz bilgiler için, detaylı inceleyeceğim şimdi.

    Benim aslında yapmak istediğim şu. Dünden beri çıkamadım işin içinden.

    Quiz projesinde sınava giren kişiye sırayla soruları gösteriyorum. Soruya cevap verip sonraki soruya geçmek istediğinde 3 saniye kadar bekletip sonraki soruya geçiriyorum. Sorularda da çözme süresi mevcut.

    Problem şu: Sonraki soruya geçerken zamanlayıcı anlam veremediğim şekilde bekleme süresini hızlı bir şekilde geçiyor. Normalde 3 saniye bekleyecekken sanki 2'şer kere atlıyor ve 1 buçuk saniyede geçiyor gibi oluyor.

    Başlarken sıkıntı yok ilk soruda zamanlayıcı normal bir şekilde çalışıyor, ancak diğer sorulara geçerken soru yükleniyor kısmında dengesizlik oluşuyor. Soru ekrana geldiğinde sağ üst taraftaki sayaç normal çalışıyor.

    Şöyle göstereyim https://s4.gifyu.com/images/test04cac028d4412135.gif

    Sizin verdiğiniz örnekten kodu şu hale getirdim ancak yine çözüme ulaşamadım

                this.timer = 3
                this.loading.text = (this.stats.answerQuestions.length + 1) + '. Soru yükleniyor'
                this.loading.state = true
    
                function sleep(t) {
                    return new Promise((resolve, reject) => {
                        const i = setInterval(() => {
    
                            --t.timer
                            if (t.timer < 1) {
                                clearInterval(i);
                                resolve();
                            }
    
                        }, 1000);
                    });
                }
    
                function loadQuestion(t) {
    
                    t.loading.text = null
                    t.loading.state = false
    
                    const question = t.quiz.questions[t.stats.answerQuestions.length]
                    t.question = {
                        data: question,
                        answer: null,
                        beginTime: Date.now(),
                        endTime: null
                    }
    
                    t.timer = t.question.data.time
    
                }
    
                async function threadQueue(t) {
                    await sleep(t);
                    loadQuestion(t);
                    await sleep(t);
                }
    
                threadQueue(this);

    Sonraki soruya geçildiğinde yükleme ekranında this.timer'ı 2 kere eksiltiyor gibi. setIntervalin biri bitmeden diğeride aynı anda çalışıyor gibi görünüyor. Ancak konsoldan bakıncada herşey normal görünüyor. Sırayla süreleri alıyor ve çalıştırıyor.

    Teşekkür ederim verdiğiniz bilgiler için

      DreamerBird timer değişkenini obje (this) içine özellik olarak eklemişsiniz, mutable çalışıyor, her --t.timer yapıldığında orjinal değeri değişiyor, eksiliyor. Bir sonraki sleep içine eksilerek başlıyor. Her soru arasında 3 saniye beklemesini istiyorsanız zaten böyle bir şey yapmanıza gerek yok. Verdiğim örneğe bakın:

      function sleep() {
        return new Promise((resolve, reject) => {
            const i = setInterval(() => {
              clearInterval(i);
              resolve();
            }, 3000); // Burada 3 saniyeyi belirliyoruz zaten
        });
      }

      Yaptığınıza bakılırsa zaten setInterval kullanmanıza gerek yok, setTimeout kullanın:

      function sleep() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve();
            }, 3000); // Burada 3 saniyeyi belirliyoruz zaten
        });
      }

      Hocam timer'da belirlenen süre kadar bekliyor, 3 saniye sadece yüklenme ekranında bekleme süresi. Soru çözme süreleri değişiyor bunun için timer içindeki süre kadar bekletiyor orda. Birde ekranda yazmam lazım kalan zamanı bu yüzden intervalde eksiltiyorum.