I am trying to create a timer with while loops and setTimeout but everytime i give an input the code crashes. İ thought doing it this way would work but seems like it doesnt. What is the problem here? And how do i achieve what i want with while loop and setTimeout?
function countdown(durationInput) {
while (durationInput > 0) {
setTimeout(() => {
console.log(durationInput)
durationInput = durationInput - 1
console.log(durationInput)
}, 1000)
}
}
Well look at this image. What you see is the basics of how javascript works.
JS is single threaded. That means 1 core do the work. Thats the event loop.
The event loop grabs the event from the callback queue and put its on the call stack and executes it.
If you run setTimeout you put it into the callback que
The problem: When the event loop is blocked by an synchronouse task like your while loop, that means the event loop cannot grab the events to the stack, because its blocked.
What happens is: The while loop runs and runs and adds more and more setTimeout events to the callback que until the programm crashes.
1 possible solution could be to use promises:
async function sleep(ms) {
return new Promise(res => {
setTimeout(res, ms)
})
}
async function sleep(ms) {
return new Promise(res => {
setTimeout(res, ms)
})
}
async function main() {
console.log("waiting 5 seconds without blocking the main thread")
await sleep(5000)
console.log("done")
}
main()
The problem is that you are generating an infinite loop, as the durationInput doesn't update until the setTimeout executes. And you are filling the execution stack with a huge cue.
For fixing it, you need to create a variable that will store the initial value of the durationInput and subtract from duration in the while loop.
function countdown(duration, intervalInSeconds = 1) {
let count = duration;
while (duration > 0) {
setTimeout(() => {
console.log(count--);
}, intervalInSeconds * 1000);
duration--;
}
}
countdown(10);
Related
how can i define a function, that is called asynchronously?
async function getPosts() {
for (i = 0; i < 1000000000; i++){}
console.log("loop done.");
}
console.log("start");
getPosts();
console.log("end");
the result is:
start
loop done.
end
i was expecting:
start
end
loop done
i expect that, because the function "getPosts" is declared as "async".
so the we dont wait until the function is finished.
How do i have to adjust my code to get the expected result?
The async function could be the one calling another function that returns a Promise. Inside the Promise you could put a setTimeout instead of the for loop and finall make the call with asyncCall:
function getPosts() {
return new Promise(resolve => {
setTimeout(() => {
resolve('done.');
}, 2000);
});
}
async function asyncCall() {
var result = await getPosts();
console.log(result);
}
console.log("start");
asyncCall();
console.log("end");
The main reason why your code don't do the "async" thing, it's because of the way js threads works. A FOR loop will use the main thread to do the job soooo, the code will wait it finish de loop. The #shys answer works, but without the loop.
If you want a working async, take a look at the WebWorker api
To sum up what Mauricio Sipmann and shrys correctly told: An asynchronous function is a function which operates asynchronously via the event loop (see async function). In order to continue executing the calling context of your async function getPosts(), i. e. with console.log("end"), the event loop must be given a chance to pause the execution of getPosts(), e. g. with this variant of your function:
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)) }
async function getPosts() {
for (i = 0; i < 1000; i++) { await sleep(3) } // pause for 3 ms
console.log("loop done.");
}
console.log("start");
getPosts();
console.log("end");
But how is the fetch()-Method implemented?
fetch() likely contains such an await statement, so that the event loop can continue executing while fetch() is waiting for the HTTP response.
When some event happened on site (like scroll etc.) event listener function makes a search and run for all found objects async functions to make fade effect per each one.
When the event happened second time the system should sync with previous still running async functions per each object to stop them and start new one.
Removing the event listener has the issue with latest state if several events was happened till last async functions enables listener function again.
How I can found and stop previous running functions?
Timer:
function timer(ms) {
return new Promise(res => setTimeout(res, ms));
}
Async function
async function setOpacity(elem, newOp)
{
// Set setOp to previous value
do{
await timer(time);
setOp = setOp + diff;
elem.style.opacity = setOp;
} while(setOp < newOp);
}
Event listener:
function fadeimg()
{
fadeImg = document.getElementsByClassName("picture");
// Calc opacityToSet;
for (var i =0; i<fadeImg.length; i++)
{
var position = (fadeImg[i].offsetTop - scrolled);
setOpacity(fadeImg[i],opacityToSet);
} // for
}
I need to call some functions sequentially, and in order to force the framework to do a dirty check after each step, I employed setTimeout(func,0) to trigger the dirty check mechanism.
I know simply calling setTimeout one by one won't guarantee the passed async functions would be invoked in expected order, therefore I kludged the following solution:
function foo(arg){
setTimeout(()=>console.log('executing task' + arg),0);
console.log('on call stack' + arg);
return foo;
}
I tried foo(1)(2)(3)(4)(5), it works fine. But I'm not sure it will always work correctly.
So could anybody please help me with it!
#Steffomio's answer can definitely make the queued task deterministic, it also makes sure that each task has its own event loop ensuing.
Here is my adapted version:
function queueTask(task) {
var queue = [];
function nextTask() {
setTimeout(function () {
queue.length && queue.shift()(); taskCount++;
queue.length && nextTask();
}, 0);
}
return (function pushTask(task) {
queue.push(task);
//After the first call trigger the timeout asynchrony
if (queue.length === 1) { nextTask(); }
return pushTask;
})(task);
}
//Test part below
function t(arg) { return function () { console.log('Task ' + arg); } }
var taskCount = 0;
var beginTime = Date.now();
queueTask(t(1))(t(2))(t(3))(t(4))(t(5))
(t('a'))(t('b'))(t('c'))(t('d'))(t('e'))
(t(1))(t(2))(t(3))(t(4))(t(5))
(t('a'))(t('b'))(t('c'))(t('d'))(t('e'))
(function () { console.log(taskCount + ' tasks executed, Time elapsed: ' + (Date.now() - beginTime)); });
After some research I learned that the callback passed to setTimeout will be called by the system only when the call stack is cleared (no more code on it), thus the actual execution of queued tasks won't start until the the queuing is done, and if we queue several 0-delay timeout tasks linearly, when the next event loop starts, they will all be executed in a single run. That is not what I want! So, calling setTimeout inside the callback of the preceding setTimeout is the only way so far I know to enforce tick by tick tasks scheduling.
For better understanding, please reference the talk "What the heck is the event loop anyway" given by Philip Roberts at JSConf EU 2014
May be this kind of approach works for you
function foo(args) {
return new Promise((resolve, reject) => {
// your code based on args
if (args.length) {
console.log("processing", args[0]);
resolve(args.slice(1, args.length));
} else {
reject("finished");
}
}).then(args => {
return foo(args);
}).catch(() => {
console.log("Tasks completed...");
});
}
foo([1, 2, 3 ,4, 5]);
Output should be something like :
processing 1
processing 2
processing 3
processing 4
processing 5
Tasks completed...
You need a queue:
q = [];
addQueue(1)(2)(3)(4)(5);
addQueue('a')('b')('d');
addQueue(1)(2)(3)(4)(5);
addQueue('a')('b')('d');
addQueue(1)(2)(3)(4)(5);
addQueue('a')('b')('d');
function queue(){
if(q.length){
setTimeout(function(){
console.log('queue: ' + q.shift());
q.length && queue();
}, 1000);
}
}
function addQueue(n){
if(q.length){
q.push(n);
}else{
q.push(n);
queue();
}
return addQueue;
}
for testing copy and paste code to console.
I've been reading about setTimeout and other such timers. But I'm wondering if it's possible to work up a custom function so that all you would need to do is something like this:
//code
delay(time);
//more code
Is this possible?
UPDATE: Ok, I kind of get it. So if that isn't reasonably possible, how would you go about delaying a loop AFTER the first time. I want it to run immediately upon execution but they delay on each iteration afterward.
New UPDATE: I figure since my initial thought fell through, it might just be easier to show you the code I have.
function autoFarm (clickEvent){
var farmTargets = [
"6_300_1",
"6_300_3",
"6_300_4",
"6_300_5",
"6_300_7"];
setTimeout(function() {
$.each (farmTargets, function(index, target){
var extraData = '{"end_pos":"' + target + '","purpose":0,"upshift":1,"bring_res":{"0":0,"2":0,"1":0},"bring_ship":{"1":25,"11":0},"rate":100,"start_pos":"6_300_2"}';
var finalData = baseDataDora + extraData + "&type=1";
setTimeout(function(){
for (i = 0; i < farmTargets.length; i++){
postRequest(sendFleetURL + getSign(extraData). finalData, function(json){
});
}
}, 15000);
});//End each loop
}, 1320000);
}//End autoFarm
Basically, it should execute immediately and run the for loop 5 times on the first array element 15 seconds apart. Then 22 minutes later move to the next set and repeat for the entire array.
You can achieve something along those lines with generators. The idea is that continuation passing style (callback hell) can be flattened. The generator uses the yield keyword to pause the function, until the callback resumes it by calling its next method:
var async = function(gen) {
var g = gen()
function next(x) {
var cur = g.next(x)
if (cur.done) {
return cur.value
}
cur.value(next)
}
next()
}
var delay = function(time) {
return function(f) {
setTimeout(f, time)
}
}
async(function* () {
console.log('before')
yield delay(1000) // waits one second
console.log('middle')
yield delay(1000) // waits one second
console.log('after')
})
In CPS it would read something like:
console.log('before')
setTimeout(function() {
console.log('middle')
setTimeout(function() {
console.log('after')
}, 1000)
}, 1000)
This works in Chrome, Firefox and iojs today.
This isn't possible because of the way single-threaded event loops work. If this function were to exist, it would cause the entire UI thread to freeze until the delay was satisfied. setTimeout(cb, delay) is the nearest facility which schedules a function to be executed no earlier than the delay and at the end of the current event loop tick.
Update: Before somebody calls me on it, yes, you can theoretically engineer a delay function that freezes everything in place for a set amount of time. However, there is no reasonable excuse to do it this way.
To your second question:
function hello() {
console.log('hello');
}
// execute immediately
hello();
// then every 5 seconds
setInterval(hello, 5000);
As-written, no that's not possible.
If, instead you were to use a queue, delays in that manner are trivial.
jQuery's .queue() and .delay() functions are a good example of how this works, so I will use them as an example, however the general point stands for any queueing library.
Instead of:
//code
delay(time)
//more code
With a queue, you'd write:
$('...') //some selector to act on for jQuery
.queue(function (next) {
//code
//Indicate that the queued call is finished.
next();
//This allows async code to be executed in the queue,
//such as ajax and animations
})
.delay(time)
.queue(function (next) {
//more code
next();
});
Now, even if you ignore the lines used for comments, you can tell that there's a bit more boilerplate to achieve the desired behavior. I don't feel that it's excessive, because I find it relatively easy to read:
queue something to happen
wait for some number of milliseconds
queue something else to happen
Using a Promise, calling it inside an asynchronous function.
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const any_function = async() => {
await delay(2000);
console.log('this log has been delayed 2 secs')
}
I have a code which needs to be executed after some delay say 5000 ms.Currently I am using setTimeout but it is asynchronous and i want the execution to wait for its return. I have tried using the following:
function pauseComp(ms)
{
var curr = new Date().getTime();
ms += curr;
while (curr < ms) {
curr = new Date().getTime();
}
}
But the code i want to delay is drawing some objects using raphaeljs and the display is not at all smooth. I am trying to use doTimeout plugin. I need to have a delay only once as the delay and code to be delayed are both in a loop. I have no requirement for a id so I am not using it.
For example:
for(i; i<5; i++){ $.doTimeout(5000,function(){
alert('hi'); return false;}, true);}
This waits for 5 sec befor giving first Hi and then successive loop iterations show alert immediately after the first. What I want it to do is wait 5 sec give alert again wait and then give alert and so on.
Any hints/ suggestions are appreciated!
Variation on the accepted answer which is just as good as this one.
Also, I agree with the caveats of preferring setTimeout and asynchronous function calling but sometimes e.g., when building tests, you just need a synchronous wait command...
function wait(ms) {
var start = Date.now(),
now = start;
while (now - start < ms) {
now = Date.now();
}
}
if you want it in seconds, divide start ms by 1000 on the while check...
=== EDIT ===
I noticed that my answer has bubbled to the top but it really shouldn't be the top answer. That was written as an alternative in case you cannot use async / await in your code or you're waiting for a trivial amount of time (like a second or two for testing).
The top answer should note that the async/await pattern is a much better way of doing this and will significantly use less energy and CPU cycles.
See #michaelolof 's answer below for example....
const wait = (msec) => new Promise((resolve, _) => {
setTimeout(resolve, msec));
});
(async () => {
console.log("Start...")
await wait(5000);
console.log("...End")
})();
If you'd like to take advantage of the new async/await syntax, You can convert set timeout to a promise and then await it.
function wait(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("Done waiting");
resolve(ms)
}, ms )
})
}
(async function Main() {
console.log("Starting...")
await wait(5000);
console.log("Ended!")
})();
Synchronous wait (only for testing!):
const syncWait = ms => {
const end = Date.now() + ms
while (Date.now() < end) continue
}
Usage:
console.log('one')
syncWait(5000)
console.log('two')
Asynchronous wait:
const asyncWait = ms => new Promise(resolve => setTimeout(resolve, ms))
Usage:
(async () => {
console.log('one')
await asyncWait(5000)
console.log('two')
})()
Alternative (asynchronous):
const delayedCall = (array, ms) =>
array.forEach((func, index) => setTimeout(func, index * ms))
Usage:
delayedCall([
() => console.log('one'),
() => console.log('two'),
() => console.log('three'),
], 5000)
Using the new Atomics API, you can start synchronous delays without performance spikes:
const sleep = milliseconds => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds)
sleep(5000) // Sleep for 5 seconds
console.log("Executed after 5 seconds!")
JavaScript is a single-threaded language. You cannot combine setTimeout and synchronous processing. What will happen is, the timer will lapse, but then the JS engine will wait to process the results until the current script completes.
If you want synchronous methods, just call the method directly!
If you want to process something after the setTimeout, include it or call it from the timeout function.
Non-timeout loops (that check the time or count to 1000000 or whatever) just lock up the browser. setTimeout (or the $.doTimeout plugin) is the best way to do it.
Creating timeouts within a loop won't work because the loop doesn't wait for the previous timeout to occur before continuing, as you've discovered. Try something more like this:
// Generic function to execute a callback a given number
// of times with a given delay between each execution
function timeoutLoop(fn, reps, delay) {
if (reps > 0)
setTimeout(function() {
fn();
timeoutLoop(fn, reps-1, delay);
}, delay);
}
// pass your function as callback
timeoutLoop(function() { alert("Hi"); },
5,
5000);
(I just cobbled this together quickly, so although I'm confident that it works it could be improved in several ways, e.g., within the "loop" it could pass an index value into the callback function so that your own code knows which iteration it is up to. But hopefully it will get you started.)
I have made a simple synchronous timeout function. It works in two different ways, callback and non-callback.
function:
function wait(ms, cb) {
var waitDateOne = new Date();
while ((new Date()) - waitDateOne <= ms) {
//Nothing
}
if (cb) {
eval(cb);
}
}
callback example:
wait(5000,"doSomething();");
non-callback example:
console.log("Instant!");
wait(5000);
console.log("5 second delay");
JavaScript is single-threaded
It is impossible to make a synchronous delay in javascript, simply because JavaScript is a single-threaded language. The browser (most common JS runtime environment) has what's called the event loop. So everything that the browser does happens in this very loop. And when you execute a script in the browser, what happens is:
The event loop calls your script
Executes it line by line
Once the script has finished*, the event loop continues running
Notice that all of this is happening during a single frame of the event loop! And that means that no other operation (like rendering, checking for user input, etc.) can happen before the script has exited. (*) The exception is async JavaScript, like setTimeout/Interval() or requestAnimationFrame() which are not run on the main thread. So from event loops prespective, the script has finished running.
This implies that if there were a synchronous delay in JavaScript, the whole browser would have to wait for the delay to finish, and meanwhile it's unable to do anything. So there is no, and there won't be any synchronous delay in JS.
Alternative - Maybe?
The alternative depends on the actual thing you want to do. In my case, I have a requestAnimationFrame() loop. So all I needed to do was to store the time, and check between the old time and new time in the loop.
let timer =
{
startTime: 0,
time: 1000, // time for the counter in milliseconds
restart: true // at the beginning, in order to set startTime
};
loop();
function loop()
{
if(timer.restart === true)
{
timer.startTime = Date.now();
timer.restart = false;
}
if((Date.now() - timer.startTime) >= timer.time)
{
timer.restart = true;
console.log('Message is shown every second');
// here put your logic
}
requestAnimationFrame(loop);
}
Here's how you can use the JQuery doTimeout plugin
jQuery('selector').doTimeout( [ id, ] delay, callback [, arg ... ] );
From the docs: "If the callback returns true, the doTimeout loop will execute again, after the delay, creating a polling loop until the callback returns a non-true value."
var start = Date.now();
console.log("start: ", Date.now() - start);
var i = 0;
$.doTimeout('myLoop', 5000, function() {
console.log(i+1, Date.now() - start);
++i;
return i == 5 ? false : true;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-dotimeout/1.0/jquery.ba-dotimeout.min.js"></script>
Node solution
Use fs.existsSync() to delay
const fs = require('fs');
const uuidv4 = require('uuid/v4');
/**
* Tie up execution for at-least the given number of millis. This is not efficient.
* #param millis Min number of millis to wait
*/
function sleepSync(millis) {
if (millis <= 0) return;
const proceedAt = Date.now() + millis;
while (Date.now() < proceedAt) fs.existsSync(uuidv4());
}
fs.existsSync(uuidv4()) is intended to do a few things:
Occupy the thread by generating a uuid and looking for a non-existent file
New uuid each time defeats the file system cache
Looking for a file is likely an optimised operation that should allow other activity to continue (i.e. not pin the CPU)
Inspired by #andrew65952 but more modern-like and faster
function wait(ms) {
const now = Date.now()
while (Date.now() - now <= ms) { /* do nothing */}
}
Solution using function generators. To show that it can be done. Not recommended.
function wait(miliseconds){
const gen = function * (){
const end = Date.now() + miliseconds;
while(Date.now() < end){yield};
return;
}
const iter = gen();
while(iter.next().done === false);
}
console.log("done 0");
wait(1000);
console.log("done 1");
wait(2000);
console.log("done 2");