简体   繁体   中英

setTimeout() in a loop does not wait 3 seconds between each call

I'm trying to access an API to pull stock data. The free plan only lets me make 5 calls per minute. I'm trying to use native JS/jQuery to make the calls. Using some example code I'll illustrate my issue (using 3 second intervals). I initially thought that putting setTimeout() within a for-loop that prints to the console would print, wait 3 seconds, print, wait 3 seconds, repeat... But what it does is wait 3 seconds, and then prints all the lines at once. I understand this now. It seems bad practice to "lock up" execution of a program, but I'm starting to think that is what needs to be done here?

$(function() {
    for (var i = 0; i < 5; i++) {
        doSetTimeout(i);
    }
});

function doSetTimeout(i) {
  setTimeout(function() { console.log(i); }, 3000);
}

I also tried "locking up" the program with a while-loop and time delay. But my entire browser just froze and became unresponsive for much longer than the time inputted.

function pauseExecution(miliseconds) {
  var currentTime = new Date().getTime();
     while (currentTime + miliseconds >= new Date().getTime()) {
     }
}

As others have mentioned, setTimeout is async. What is happening is you are scheduling 5 tasks to complete after roughly 3000ms . What you should do is not queue another task until the previous one has completed. This can be achieved in about a million way.

Async/Await

If you can, just use async/await . Its the easiest with the least amount of code.

 const delay = (ms) => { return new Promise((resolve) => { setTimeout(() => resolve(), ms); }, ms); }; $(async function() { for (var i = 0; i < 5; i++) { await delay(3000); console.log(i); } });
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Promise

Creating a promise chain where every task is dependent on a delay and a unit of work would serve a similar purpose as async/await if you are not transpiling ect. You have to be careful with catch ect to make this production ready.

 const delay = (ms) => { return new Promise((resolve) => { setTimeout(() => resolve(), ms); }, ms); }; $(function() { let task = delay(3000); const work = (i) => console.log(i); for (var i = 0; i < 5; i++) { let j = i; // you need this because of closures. task = task .finally(() => { work(j); }) .then(() => delay(3000)); } });
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Queues

This is probably the most complex solution. You create a blocking queue . All tasks can be queued whenever but the queue will only execute the next task after the current task calls next . This is not production ready and is meant to serve as an example. There are issues with stack overflows with sync operations as well as exception handling.

 const queue = () => { const tasks = []; let running = false; const add = (task) => { tasks.push(task); run(); }; const run = () => { if (!running) { running = true; const step = () => { if (tasks.length > 0) { tasks.shift()(step); } else { console.log('done'); running = false; } }; step(); }; }; return { add, run }; }; const q = queue(); for (let i = 0; i < 5; i++) { q.add((next) => { const j = i; // again because of closure scoping. setTimeout(() => { console.log(j); next(); }, 3000); }); }

SetInterval

If you dont need to block on previous operations(ajax/fetch) you could simply use a setInterval . This just schedules a task evey N ms regardless of the state of the previous tasks.

 let i = 0; const interval = setInterval(() => { if (i === 5) { clearInterval(interval); } else { console.log(i++); } }, 3000);

Ohh I have already done something like that, What you need to do is run and wait for the func to end.

See this example to understand.

 function doSomthing(){ console.log("Somthing is done"); Start(); } function Start(){ setTimeout(doSomthing, 3000); } Start();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM