setInterval overlapping issue

Viewed 54

I am currently developing something where both setInterval and setTimeout needs to be used. I am running setTimeout and dynamic delay is passed on to setTimeout.

While each element not being timed out from the above, I am running setInterval to print out the numbers.

The code is following

const data = [
    { "entry": "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness." },
    { "entry": "No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful." },
    { "entry": "Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure." },
    { "entry": "This is a short one." },
    { "entry": "This is line two and it is long" },
    { "entry": "This is line two and it is long" },
    { "entry": "This is line three and it is also long" },
    { "entry": "This is line four and it is very very long" },
    { "entry": "This is line five and it is the longest line of all" },
    { "entry": "This is line six and it is longer than the previous line" },
    { "entry": "B" },
    { "entry": "Who built this country hand by hand, brick by brick.  (Applause.)  They all came here knowing that what makes somebody an American is not just blood or birth, but allegiance to our founding principles and the faith in the idea that anyone from anywhere can write the next great chapter of our story." }
];

//what is the string length of each
const stringLength = [];

//get each string Length
data.forEach(
    (a, i) => {
        stringLength.push(a.entry.length);
    }
);

//container to custom define outer delay for each setTimeoutLoop
const outerDelay = [];

//predefined each inner delay
const innerDelayEach = 10;

//predefined delay
data.forEach(
    (a, i, r) => {
        (i === 0) ? outerDelay.push(0 * innerDelayEach): outerDelay.push(r[i - 1].entry.length * innerDelayEach);

    }
);

//counter for outerLoop 
let cntOuter = 0;

const elmnt = document.querySelectorAll('div');
const outerTerminator = elmnt.length - 1;
//console.log(elmnt);

function show() {

    const select = elmnt[cntOuter]; //get the element based on current counter for outerLoop    

    //construct inner loop
    //what happens till the element is not timed out

    let cntInner = 0;

    const inner = setInterval(
        function() {
            const text = select.textContent;
            const innerTerminator = text.length - 1;            
            const val = text[cntInner];
            console.log(cntOuter, cntInner, val);

            cntInner++;
            if (cntInner > innerTerminator) {
                clearInterval(inner)
            }

        }, innerDelayEach
    )

    cntOuter++; //increment the outer counter
    if (cntOuter > outerTerminator) return; // terminate outer loop when outer count > no. of divs
    setTimeout(show, outerDelay[cntOuter])

};

show();
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
  </head>
  <body>
    <div>But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness.</div>
    <div>No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful.</div>
    <div>Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure.</div>
    <div>This is a short one.</div>
    <div>This is line two and it is long</div>
    <div>This is line two and it is long</div>
    <div>This is line three and it is also long</div>
    <div>This is line four and it is very very long</div>
    <div>This is line five and it is the longest line of all</div>
    <div>This is line six and it is longer than the previous line</div>
    <div>B</div>
    <div>Who built this country hand by hand, brick by brick. (Applause.) They all came here knowing that what makes somebody an American is not just blood or birth, but allegiance to our founding principles and the faith in the idea that anyone from anywhere can write the next great chapter of our story.</div>
  </body>
  <script type="text/javascript"></script>
</html>

With the above, I expect the browser to make the callbacks from setTimeout at the delay defined and while each element is not timed out, I expect to see the setinterval printing out the character values without any overlap.

Yet, I see this overlap which I don't expect at all. I expect the cntOuter=1 to execute till cntInner=263. But it goes to cntOuter=2 while cntInner=240`.

enter image description here

Something is obviously wrong which is causing this overlap. But I don't understand what is triggering that.

1 Answers

It is somewhat hard to reason about your code. One thing I noticed was that inner loop is executed every outerTerminator milliseconds, which seems to depend on the number of entries you have. I would have expected the inner loop to delay for innerDelayEach milliseconds, not outerTerminator milliseconds.

A much easier and readable way would be to use async/await and simply have two nested loops with a delay:

const data = [
    { "entry": "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness." },
    { "entry": "No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful." },
    { "entry": "Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure." },
    { "entry": "This is a short one." },
    { "entry": "This is line two and it is long" },
    { "entry": "This is line two and it is long" },
    { "entry": "This is line three and it is also long" },
    { "entry": "This is line four and it is very very long" },
    { "entry": "This is line five and it is the longest line of all" },
    { "entry": "This is line six and it is longer than the previous line" },
    { "entry": "B" },
    { "entry": "Who built this country hand by hand, brick by brick.  (Applause.)  They all came here knowing that what makes somebody an American is not just blood or birth, but allegiance to our founding principles and the faith in the idea that anyone from anywhere can write the next great chapter of our story." }
];

function sleep(delay) {
  return new Promise((resolve, reject) => setTimeout(resolve, delay));
}

async function type(data, delay) {
  const container = document.querySelector('#container');
  for (const item of data) {
    const div = document.createElement('div');
    container.appendChild(div);
    for (const c of [...item.entry]) {
      div.textContent += c;
      await sleep(delay);
    }
  }
}

type(data, 10);
<div id="container"></div>

Related