Designer
Designer

Reputation: 35

Set time out 5 seconds for one by one number in array, but not for all at once

I have array with 100 numbers, i want to add active class to 20 of them, that part is ok and working. But i want delay between every number.

I tried with settimeout, but it only delays for 5 seconds all numbers at once, but i want to set active class one by one with delay of 5 seconds. Please help.

function numbers() { 
                    
                        var activequantities;
                    activequantities = "@Html.DisplayFor(modelItem => item.Numbers)".split(",");
// this is array with sorted random numbers 20 of 100 example [22,33,46,57,etc]


                    function setClassNextItem(index)
{
    if (index >= activequantities.lenght) return;

    var value = activequantities[index];
    $(`.grid-container div[data-tabid=${value}]`).addClass('active');
    setTimeout(setClassNextItem(++index), 5000);
}

$(".grid-container div").removeClass('active');
                    setTimeout(setClassNextItem(0), 5000);

                        
                        
                }

<div class="grid-container">
        <div class="grid-item">

            <div class="grid-container2">
                <div class="grid-item2" data-tabid="1">1</div>
                <div class="grid-item2" data-tabid="2">2</div>
                <div class="grid-item2" data-tabid="3">3</div>
                <div class="grid-item2" data-tabid="4">4</div>
                </div>

Add active class one by one. With delay of 5 seconds between each number.

Upvotes: 0

Views: 683

Answers (4)

dx_over_dt
dx_over_dt

Reputation: 14318

Drew's answer is the best and simplest so far. Another option is to go the async function/promise route.

/**
 * Time in milliseconds to wait between setting elements to active.
 */
const delayTime = 500, // shortened to half a second for the example
  $app = $('#app');

/**
 * Whether or not the routine is currently running.
 */
let stopped = true;

/**
 * Returns a promise that resolves after the specified number of milliseconds.
 */
function delay(ms) {
  return new Promise(res => setTimeout(res, ms));
}

function setActiveInSerial() {
  // a little user-friendly UI work
  stopped = !stopped;
  if (stopped) {
    $('#btn').text('Run');
  } else {
    $('#btn').text('Stop');
  }

  // promise that lets us serialize asynchronous functions
  let promise = Promise.resolve();

  $app.children().each(function() {
    promise = promise.then(async() => {
      if (stopped) {
        return;
      }

      $(this).addClass('active');

      // wait for `delayTime` to pass before resolving the promise
      await delay(delayTime);
      
      $(this).removeClass('active');
    })
  });
  
  // mark the routine as stopped and reset the button
  promise.then(() => {
    stopped = true;
    $('#btn').text('Run');
  });
}

// populate with items
for (let i = 1; i <= 20; i++) {
  $app.append($('<span>').text(`Item ${i}`));
}

// attach function to run button
$('#btn').click(setActiveInSerial);
#app {
  display: flex;
  flex-direction: column;
}

#app .active {
  background-color: cyan;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="btn">Run</button>
<div id="app"></div>

This code is obviously longer, but it's more flexible, and it's good practice for learning promises and async functions. It has the added benefit of not needing to search through all of the children of your element in order to remove the active class.

Upvotes: 0

Nicolas
Nicolas

Reputation: 8670

Welcome to stackoverflow! The function setTimeout returns an Async Promise that will resolve in x ms. If you run this function in a loop, you simply add more promises to be resolved, but they will all resolve close to the same time. Ex:

Send first Promise : it will wait 5 seconds
One miliseconds later, send another promise that will wait 5 second
...
5 seconds later the first Promise resolve correctly.
1 ms later, the second promise resolve correctly
...

This behaviour makes all of your promises resolve almost at the same time, just 5 seconds after you initialized them.

In your case, since you want to execute a function at a precise interval, you need the setInterval. The setInterval function also takes a time in MS and a function but it repeat this function every x ms, or until the interval is stopped programmaticaly. EX:

var counting = 0;
var myInterval = setInterval(function() {
    //we do something...
    console.log('loop:' + counting ++);
    if(counting === 10) {
        // we do this 10 times, than we stop the interval
        clearInterval(myInterval);
    }
}, 1000);

Here I'm using 1sec interval because 5 is way too long for a demo.

Upvotes: 0

Drew Reese
Drew Reese

Reputation: 202915

Iterate through your filtered elements array and add an (index + 1) * interval timeout for the callback to add the classname.

for (i = 0; i < 20; i++) {
  setTimeout(
    function (i) {
      //$this.addClass("active");
      console.log(`Add "active" class to element ${i}`);
    },
    (i + 1) * 1000, // shortened to 1 second for demo
    i
  );
}

Upvotes: 1

dev-cc
dev-cc

Reputation: 454

Try to convert activequantities to a numbers array and do something like this:

function setClassNextItem(index)
{
    if (index >= activequantities.lenght) return;

    var value = activequantities[index];
    $(`.grid-container div[data-tabid=${value}]`).addClass('active');
    setTimeout(setClassNextItem(++index), 5000);
}

$(".grid-container div").removeClass('active');
setTimeout(setClassNextItem(0), 5000);

Upvotes: 0

Related Questions