Is setTimeout required? - javascript

I have a question to async, await and setTimeout().
I thought, I use asynchron functions for slow processes. So I tried it with a large loop. On my Computer, it needs few seconds to run the following code:
function slowFunction() {
return new Promise(resolve => {
setTimeout(() => {
for (let i = 0; i < 4000000000; i++) {};
resolve('Ready at ' + new Date().toLocaleTimeString('de'));
}, 0);
});
};
console.log('Start: ' + new Date().toLocaleTimeString('de'));
(async () => {
console.log('Call slow function.');
console.log(await slowFunction());
})();
console.log('There is no need to wait for the slow function: ' + new Date().toLocaleTimeString('de'));
The output is:
Start: 16:39:20
Call slow function.
There is no need to wait for the slow function: 16:39:20
Ready at 16:39:23
And now the question: What is the difference to the next code:
function slowFunction() {
return new Promise(resolve => {
for (let i = 0; i < 4000000000; i++) {};
resolve('Ready at ' + new Date().toLocaleTimeString('de'));
});
};
console.log('Start: ' + new Date().toLocaleTimeString('de'));
(async () => {
console.log('Call slow function.');
console.log(await slowFunction());
})();
console.log('There is no need to wait for the slow function: ' + new Date().toLocaleTimeString('de'));
The output is:
Start: 16:39:20
Call slow function.
There is no need to wait for the slow function: 16:39:23
Ready at 16:39:23
By the first example, it looks like asynchron. By the second example, the function wait for the end of loop.
Do I have to use setTimeout or do I have an error in the code or am I getting it wrong? I both cases, the resolve statment is behind the large loop.
The most examples for async and await used setTimeout, but I think, it's just to simulate a break.
Thanks for your help in advance.
Best greets
Pascal

TL:DR
Promises and async functions don't offload your code to another thread. If you want to move that long-running process off the main thread, on browsers look at web workers, and on Node.js look at child processes.
Details
Promises and async functions (which are just a syntax for creating and consuming promises) don't move your processing to any other thread, it still happens on the same thread you start the process on. The only thing they do is ensure that then and catch callbacks are called asynchronously. They don't make your code asynchronous (other than that one thing, ensuring the callbacks happen asynchronously).
So your first block using setTimeout just sets a timeout, returns a promise, and then when the timeout expires it blocks the main thread while your slow-running process executes. This just changes when the blocking happens a little bit, it doesn't change the fact of the blocking.
You can see that effect here, notice how the counter pauses when the long-running process occurs:
function slowFunction() {
return new Promise(resolve => {
setTimeout(() => {
const stop = Date.now() + 2000;
while (Date.now() < stop) {
// busy wait (obviously, never really do this)
}
}, 1000);
});
};
console.log("before slowFunction");
slowFunction()
.then(() => {
console.log("then handler on slowFunction's promise");
})
.catch(console.error);
console.log("after slowFunction");
let counter = 0;
const timer = setInterval(() => {
console.log(++counter);
}, 100);
setTimeout(() => {
clearInterval(timer);
console.log("done");
}, 3000);
.as-console-wrapper {
max-height: 100% !important;
}
Your second block not using setTimeout just blocks right away, because the promise executor function (the function you pass new Promise) runs immediately and synchronously, and you're not doing anything to make it asynchronous.
You can see that here; the counter pauses right away, not later:
function slowFunction() {
return new Promise(resolve => {
const stop = Date.now() + 2000;
while (Date.now() < stop) {
// busy wait (obviously, never really do this)
}
});
};
console.log("before slowFunction");
slowFunction()
.then(() => {
console.log("then handler on slowFunction's promise");
})
.catch(console.error);
console.log("after slowFunction");
let counter = 0;
const timer = setInterval(() => {
console.log(++counter);
}, 100);
setTimeout(() => {
clearInterval(timer);
console.log("done");
}, 3000);
.as-console-wrapper {
max-height: 100% !important;
}
We don't even see the before slowFunction log appear until after the long-running code has finished, because the browser never got a chance to repaint, we had the thread hogged.
Regarding async functions: The code in an async function starts out synchronous, and is synchronous until the first await (or other construct, such as setTimeout, that schedules things to execute later). Only the code after that is asynchronous (because it had to wait).
Here's an example demonstrating that:
async function foo() {
console.log("before await");
await Promise.resolve();
console.log("after await");
}
console.log("before foo");
foo()
.then(() => {
console.log("then handler on foo's promise");
})
.catch(console.error);
console.log("after foo");
Here's the output of that:
before foo
before await
after foo
after await
then handler on foo's promise
Notice how before await occurs before after foo; it's synchronous with the call to foo. But then after await doesn't occur until later (because await Promise.resolve() has to make the code following it occur asynchronously; it's syntactic sugar for then, which promises not to call its handler synchronously even if the promise is already resolved).

The difference is that this is completely synchronous code:
return new Promise(resolve => {
for (let i = 0; i < 4000000000; i++) {};
resolve('Ready at ' + new Date().toLocaleTimeString('de'));
});
This statement will block the JavaScript thread and force it to wait until all of those 4 billion iterations have taken place. Then it will move on to the next statement. Since the console.log executes after this, it will not execute until that loop has finished.
That's why you are seeing a difference.

Related

What is the behaviour when an un-awaited async function is followed by an awaited async function in interms of execution order of the async functions?

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?

Why does this loop repeat each iteration twice?

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 to define a async-Function?

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.

Control while loop by Promise then function

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

Synchronous delay in code execution

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");

Categories

Resources