Sample code:
async function foo(i) {
await new Promise(resolve => setTimeout(resolve, 5000));
console.log('Completed foo', i);
}
async function bar(i) {
await new Promise(resolve => setTimeout(resolve, 5000));
console.log('Completed bar', i);
}
for (let i = 0; i < 10; i++) {
foo(i);
console.log('Fired', i);
await bar(i);
}
console.log('Done firing all async functions, unblocking...');
When I run this I get an error in the last line of the for loop indicating I can only await inside an async function.
Anyways, this question is to ask - when the for loop's 1st iteration is being executed, foo will immediately be added to the task queue. Then console log will run printing Fired. Then it will add bar to the task queue and await for the response. Hence block the for loop iterations until the bar execution completes.
At this point, Main thread is free. What is the behaviour in terms of whether foo or bar will be picked up from the task queue and put into the stack for execution by the main thread?
The function below prints each number twice. Could someone explain how it works? I tried debugging but all I can see is that the value of i only increases on every second iteration.
async function run(then) {
for (let i = 1; i <= 10; i++) {
console.log(i);
then = await { then };
}
}
run(run);
Concretely speaking, there are two things I don't understand.
Why does i not increase on every single iteration?
What does then = await { then }; exactly do? My first guess was that it would wait for a nested async call to run to finish before moving on to the next iteration, but this does not seem to be the case.
We can make this a bit clearer with minor re-write to include logging:
async function run(callback) {
let then = callback;
for (let i = 1; i <= 10; i++) {
console.log(callback === run ? "A" : "B", i);
then = await { then };
}
}
run(run);
.as-console-wrapper { max-height: 100% !important; }
This shows there are actually two loops started. For simplicity just called A and B. They log and await which means that their logs interleave and lead to A 1, B 1, A 2, B 2, etc.
This happens because of the first statement: run(run). Which passes the same function as a callback to itself. This does not call the callback but it is the first step to unravelling this.
The next step to understanding what is happening is await. You can await any value and in most cases if it's not a promise, it doesn't matter. If you have await 42; it just pretends the value was Promise.resolve(42) and continues the operation immediately with the next tick. That is true for most non-promises. The only exception is thenables - objects which have a .then() method.
When a thenable is awaited, its then() method is called:
const thenable = {
then() {
console.log("called");
}
};
(async () => {
await thenable;
})()
Which then explains the await { then } statement. This uses the shorthand for { then: then } where then is the callback passed to run. Thus it creates a thenable object which, when awaited, will execute the callback.
This means that the first time run() is executed and on the first iteration of the loop A the code is effectively await { then: run } which will execute run again which then starts loop B.
The value of then is overridden each time, hence why it only ever runs two loops in parallel, rather than more.
There is more to thenables that is relevant to fully grasp this code. I showed a simple one before which just shows that awaiting it calls the method. However, in reality await thenable will call .then() with two parameters - functions that can be called for success and failure. In the same way that the Promise constructor does it.
const badThenable = {
then() {
console.log("bad called");
}
};
(async () => {
await badThenable;
console.log("never reached");
})();
const goodThenable = {
then(resolve, reject) { //two callbacks
console.log("good called");
resolve(); //at least one needs to be called
}
};
(async () => {
await goodThenable;
console.log("correctly reached");
})();
This is relevant because run() expects a callback and when the await { then: run } executes it calls run(builtInResolveFunction) which then gets passed to the next await { then: builtInResolveFunction } which in turn resolves causes the a await to resolve.
With all this aside, the interleaved logging is just a factor of how tasks resolve:
(async () => {
for (let i = 1; i <= 10; i++){
console.log("A", i);
await Promise.resolve("just to force a minimal wait");
}
})();
(async () => {
for (let i = 1; i <= 10; i++) {
console.log("B", i);
await Promise.resolve("just to force a minimal wait");
}
})();
If there are two async functions running and there is nothing to really wait for:
One would run until it reaches an await and will then be suspended.
The other would run until it reaches an await and will then be suspended.
Repeat 1. and 2. until there are no more awaits.
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.
I have a function that returns Promise
let processWork = function () {
return new Promise(resolve => {
console.log('- internal: start')
// just wait 200ms for example
let future = new Date(new Date().getTime() + 200)
while (future > new Date()) {}
console.log('- internal: done')
resolve()
})
}
I call it and use then function to change variable to break endless while but not work
let doing = false
let done = false
while (!done) {
if (doing) {
continue
}
console.log('Starting work')
doing = true
processWork()
.then(() => {
console.log('Finished')
done = true
})
}
I get output
Starting work
- internal: start
- internal: done
and my code's still running forever.
My question is why then function not running in this case.
It is not true that a promise will somehow get resolved while a busy loop is being executed. As long as a loop is executing no other JavaScript will execute (excluding web workers). The following loop will always execute the continue statement in every iteration and will never finish.
let doing = false
let done = false
while (!done) {
if (doing) {
continue
}
// ...
}
No promise could help interrupt this loop, but in your case, there is not even a promise since processWork never gets executed.
If processWork would get executed, the loop in the promise constructor callback will lock up the browser until the target time arrives: no other JavaScript gets executed during that delay. This defeats the purpose of promises: they are intended not to lock up the browser and allow the execution of other code while the promise is still pending.
So, do it like this:
let processWork = function () {
return new Promise(resolve => {
console.log('- internal: start delay of 200ms')
// just wait 200ms for example, but don't stop execution of other code
setTimeout(resolve, 200);
})
}
console.log('Starting work')
processWork().then(() => {
console.log('Promise resolved. Finished');
});
console.log('Waiting for promise to resolve...');
console.log('In the meanwhile let\'s calculate 10!');
let fact = 1;
for (let i = 1; i <= 10; i++) {
fact *= i;
}
console.log('10! = ', fact);
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");