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.
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>
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>
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); }); }
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.