How To Stop An Infinite Loop in Node.js/Javascript - javascript

If we started 2 concurrent infinite loops using worker('hello') and worker('world'), how can we later stop one of the loops?
For example:
const sleep = async function (duration) {
await new Promise(r => setTimeout(r, duration));
}
const worker = async (id) => {
while (true) {
console.log(id);
await sleep(2000); // simulates a blocking call
}
}
(async () => {
const hello = worker('hello')
const world = worker('world')
// Let's assume that now a user-input requires us to stop the `worker('hello')`
setTimeout(() => {
console.log('stopping hello...')\
// how to stop 'hello'?
}, 5000)
})();

You cannot stop those worker() loops from outside of the function. Javascript does not have that capability.
You would need those loops to be checking something that is outside the loop (a variable or calling a function or something like that) for you to be able to influence them.
There are many other ways to write the loop that can be influenced from the outside world.
Some examples:
Use setInterval() and return the interval timerID from the function. Then, you can call clearInterval() to stop the loop.
Create a small object where your loop is one method and have that loop test an instance variable that you can change from the outside.
P.S. There might be some hacks where you replace Promise with a constructor that would force a reject which would cause the await to throw and then containing async function to reject on the next cycle, but I assume you're not looking for that level of hack and invasion of the environment.
Since sleep() is declared as const you can't hack in a replacement for it that would reject.

If the only thing you want to do with the worker function is to repeat some action every N milliseconds, I suggest using setInterval explained here
function worker(id) {
return setInterval(() => {//loop actions inside this annonymous function
console.log(id);
//Anything else
}, 2000);//Every 2000 milliseconds
}
//make a loop active
const intervalHello = worker(`Hello`);
//stop the interval
clearInterval(intervalHello);

Related

How to set delay or interval inside map Promise? [duplicate]

I'm developing a console script for personal needs. I need to be able to pause for an extended amount of time, but, from my research, Node.js has no way to stop as required. It’s getting hard to read users’ information after a period of time... I’ve seen some code out there, but I believe they have to have other code inside of them for them to work such as:
setTimeout(function() {
}, 3000);
However, I need everything after this line of code to execute after the period of time.
For example,
// start of code
console.log('Welcome to my console,');
some-wait-code-here-for-ten-seconds...
console.log('Blah blah blah blah extra-blah');
// end of code
I've also seen things like
yield sleep(2000);
But Node.js doesn't recognize this.
How can I achieve this extended pause?
Update Jan 2021: You can even do it in the Node REPL interactive using --experimental-repl-await flag
$ node --experimental-repl-await
> const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
> await delay(1000) /// waiting 1 second.
A new answer to an old question. Today ( Jan 2017 June 2019) it is much easier. You can use the new async/await syntax.
For example:
async function init() {
console.log(1);
await sleep(1000);
console.log(2);
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
For using async/await out of the box without installing and plugins, you have to use node-v7 or node-v8, using the --harmony flag.
Update June 2019: By using the latest versions of NodeJS you can use it out of the box. No need to provide command line arguments. Even Google Chrome support it today.
Update May 2020:
Soon you will be able to use the await syntax outside of an async function. In the top level like in this example
await sleep(1000)
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
The proposal is in stage 3.
You can use it today by using webpack 5 (alpha),
More info:
Harmony Flag in Nodejs: https://nodejs.org/en/docs/es6/
All NodeJS Version for download: https://nodejs.org/en/download/releases/
The shortest solution without any dependencies:
await new Promise(resolve => setTimeout(resolve, 5000));
Best way to do this is to break your code into multiple functions, like this:
function function1() {
// stuff you want to happen right away
console.log('Welcome to My Console,');
}
function function2() {
// all the stuff you want to happen after that pause
console.log('Blah blah blah blah extra-blah');
}
// call the first chunk of code right away
function1();
// call the rest of the code and have it execute after 3 seconds
setTimeout(function2, 3000);
It's similar to JohnnyHK's solution, but much neater and easier to extend.
This is a simple blocking technique:
var waitTill = new Date(new Date().getTime() + seconds * 1000);
while(waitTill > new Date()){}
It's blocking insofar as nothing else will happen in your script (like callbacks). But since this is a console script, maybe it is what you need!
Put the code that you want executed after the delay within the setTimeout callback:
console.log('Welcome to My Console,');
setTimeout(function() {
console.log('Blah blah blah blah extra-blah');
}, 3000);
On Node 7.6.0 or higher
Node supports waiting natively:
const sleep = (waitTimeInMs) => new Promise(resolve => setTimeout(resolve, waitTimeInMs));
then if you can use async functions:
await sleep(10000); // sleep for 10 seconds
or:
sleep(10000).then(() => {
// This will execute 10 seconds from now
});
On older Node versions (original answer)
I wanted an asynchronous sleep that worked in Windows & Linux, without hogging my CPU with a long while loop. I tried the sleep package but it wouldn't install on my Windows box. I ended up using:
https://www.npmjs.com/package/system-sleep
To install it, type:
npm install system-sleep
In your code,
var sleep = require('system-sleep');
sleep(10*1000); // sleep for 10 seconds
Works like a charm.
Simple and elegant sleep function using modern Javascript
function sleep(millis) {
return new Promise(resolve => setTimeout(resolve, millis));
}
No dependencies, no callback hell; that's it :-)
Considering the example given in the question, this is how we would sleep between two console logs:
async function main() {
console.log("Foo");
await sleep(2000);
console.log("Bar");
}
main();
The "drawback" is that your main function now has to be async as well. But, considering you are already writing modern Javascript code, you are probably (or at least should be!) using async/await all over your code, so this is really not an issue. All modern browsers today support it.
Giving a little insight into the sleep function for those that are not used to async/await and fat arrow operators, this is the verbose way of writing it:
function sleep(millis) {
return new Promise(function (resolve, reject) {
setTimeout(function () { resolve(); }, millis);
});
}
Using the fat arrow operator, though, makes it even smaller (and more elegant).
You can use this www.npmjs.com/package/sleep
var sleep = require('sleep');
sleep.sleep(10); // sleep for ten seconds
If you want to "code golf" you can make a shorter version of some of the other answers here:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
But really the ideal answer in my opinion is to use Node's util library and its promisify function, which is designed for exactly this sort of thing (making promise-based versions of previously existing non-promise-based stuff):
const util = require('util');
const sleep = util.promisify(setTimeout);
In either case you can then pause simply by using await to call your sleep function:
await sleep(1000); // sleep for 1s/1000ms
EDIT: As noted in the comments, you can even reduce that to one line:
const sleep = require('util').promisify(setTimeout);
Or, if you don't even want to bother making a sleep function:
await require('util').promisify(setTimeout)(1000);
This question is quite old, but recently V8 has added Generators which can accomplish what the OP requested. Generators are generally easiest to use for async interactions with the assistance of a library such as suspend or gen-run.
Here's an example using suspend:
suspend(function* () {
console.log('Welcome to My Console,');
yield setTimeout(suspend.resume(), 10000); // 10 seconds pass..
console.log('Blah blah blah blah extra-blah');
})();
Related reading (by way of shameless self promotion): What's the Big Deal with Generators?.
From Node.js 15 and up you can use the Timers Promises API. You don't have to promisify setTimeout or rely on a 3rd party library anymore.
import { setTimeout } from 'timers/promises';
await setTimeout(1000);
On Linux/nodejs this works for me:
const spawnSync = require('child_process').spawnSync;
var sleep = spawnSync('sleep', [1.5]);
It is blocking, but it is not a busy wait loop.
The time you specify is in seconds but can be a fraction. I don't know if other OS's have a similar command.
Try using promise, it works for me in NodeJS
one liner
await new Promise(resolve => setTimeout(resolve, 5000));
or have it as a function in NodeJS to re-use
const sleep = async (milliseconds) => {
await new Promise(resolve => setTimeout(resolve, milliseconds));
}
use the function like
await sleep(5000)
I've recently created simpler abstraction called wait.for to call async functions in sync mode (based on node-fibers). There is also a version based on upcoming ES6 Generators.
https://github.com/luciotato/waitfor
Using wait.for, you can call any standard nodejs async function, as if it were a sync function, without blocking node's event loop.
You can code sequentially when you need it, which is, (I'm guessing) perfect to simplify your scripts for personal use.
using wait.for your code will be:
require('waitfor')
..in a fiber..
//start-of-code
console.log('Welcome to My Console,');
wait.miliseconds(10*1000); //defined in waitfor/paralell-tests.js - DOES NOT BLOCK
console.log('Blah blah blah blah extra-blah');
//endcode.
Also any async function can be called in Sync mode.
Check the examples.
Since, javascript engine (v8) runs code based on sequence of events in event-queue, There is no strict that javascript exactly trigger the execution at after specified time. That is, when you set some seconds to execute the code later, triggering code is purely base on sequence in event queue. So triggering execution of code may take more than specified time.
So Node.js follows,
process.nextTick()
to run the code later instead setTimeout(). For example,
process.nextTick(function(){
console.log("This will be printed later");
});
Node 16 has a new way to do it easily
import { setTimeout } from 'timers/promises'
console.log('before')
await setTimeout(3000)
console.log('after')
With ES6 supporting Promises, we can use them without any third-party aid.
const sleep = (seconds) => {
return new Promise((resolve, reject) => {
setTimeout(resolve, (seconds * 1000));
});
};
// We are not using `reject` anywhere, but it is good to
// stick to standard signature.
Then use it like this:
const waitThenDo(howLong, doWhat) => {
return sleep(howLong).then(doWhat);
};
Note that the doWhat function becomes the resolve callback within the new Promise(...).
Also note that this is ASYNCHRONOUS sleep. It does not block the event loop. If you need blocking sleep, use this library which realizes blocking sleep with the help of C++ bindings. (Although the need for a blocking sleep in Node like async environments is rare.)
https://github.com/erikdubbelboer/node-sleep
In order to "wait" in javascript using promises are the way to go as the top answers show.
So how can it be used?
Here's a simple example of a 5-second sub-process queuing up parameters for a 4-second main process in a non-blocking manner.
const wait = (seconds) =>
new Promise(resolve =>
setTimeout(() =>
resolve(true), seconds * 1000))
const process = async (items, prepTask, mainTask) => {
const queue = [];
let done = false;
items.forEach((item, i) => {
prepTask(item).then(() => {
queue.push(item);
if (i == items.length -1) {
done = true;
}
})
})
while (!done || queue.length) {
if (queue.length) {
const workload = queue.shift();
await mainTask(workload)
} else {
console.log('waiting for subtask to queue')
await wait(1);
}
}
}
// Usage Example
const ids = [1,2,3,4,5,6,7,8,9,10];
const prepTask = async (id) => {
await wait(id * 5)
return id * 5;
}
const mainTask = async (workload) => {
console.log('excuting workload: ', workload);
const result = await wait(4);
return { workload, result }
}
process(ids, prepTask, mainTask)
.then(() => console.log('done'))
let co = require('co');
const sleep = ms => new Promise(res => setTimeout(res, ms));
co(function*() {
console.log('Welcome to My Console,');
yield sleep(3000);
console.log('Blah blah blah blah extra-blah');
});
This code above is the side effect of the solving Javascript's asynchronous callback hell problem. This is also the reason I think that makes Javascript a useful language in the backend. Actually this is the most exciting improvement introduced to modern Javascript in my opinion. To fully understand how it works, how generator works needs to be fully understood. The function keyword followed by a * is called a generator function in modern Javascript. The npm package co provided a runner function to run a generator.
Essentially generator function provided a way to pause the execution of a function with yield keyword, at the same time, yield in a generator function made it possible to exchange information between inside the generator and the caller. This provided a mechanism for the caller to extract data from a promise from an asynchronous call and to pass the resolved data back to the generator. Effectively, it makes an asynchronous call synchronous.
This is a moment.js flavored module based on the dirty blocking approach suggested by #atlex2. Use this only for testing.
const moment = require('moment');
let sleep = (secondsToSleep = 1) => {
let sleepUntill = moment().add(secondsToSleep, 'seconds');
while(moment().isBefore(sleepUntill)) { /* block the process */ }
}
module.exports = sleep;
simple we are going to wait for 5 seconds for some event to happen (that would be indicated by done variable set to true somewhere else in the code) or when timeout expires that we will check every 100ms
var timeout=5000; //will wait for 5 seconds or untildone
var scope = this; //bind this to scope variable
(function() {
if (timeout<=0 || scope.done) //timeout expired or done
{
scope.callback();//some function to call after we are done
}
else
{
setTimeout(arguments.callee,100) //call itself again until done
timeout -= 100;
}
})();
For some people, the accepted answer is not working, I found this other answer and it is working for me: How can I pass a parameter to a setTimeout() callback?
var hello = "Hello World";
setTimeout(alert, 1000, hello);
'hello' is the parameter being passed, you can pass all the parameters after the timeout time. Thanks to #Fabio Phms for the answer.
function doThen(conditional,then,timer) {
var timer = timer || 1;
var interval = setInterval(function(){
if(conditional()) {
clearInterval(interval);
then();
}
}, timer);
}
Example usage:
var counter = 1;
doThen(
function() {
counter++;
return counter == 1000;
},
function() {
console.log("Counter hit 1000"); // 1000 repeats later
}
)
If you just need to suspend for testing purpose you current thread execution try this:
function longExecFunc(callback, count) {
for (var j = 0; j < count; j++) {
for (var i = 1; i < (1 << 30); i++) {
var q = Math.sqrt(1 << 30);
}
}
callback();
}
longExecFunc(() => { console.log('done!')}, 5); //5, 6 ... whatever. Higher -- longer
The other answers are great but I thought I'd take a different tact.
If all you are really looking for is to slow down a specific file in linux:
rm slowfile; mkfifo slowfile; perl -e 'select STDOUT; $| = 1; while(<>) {print $_; sleep(1) if (($ii++ % 5) == 0); }' myfile > slowfile &
node myprog slowfile
This will sleep 1 sec every five lines. The node program will go as slow as the writer. If it is doing other things they will continue at normal speed.
The mkfifo creates a first-in-first-out pipe. It's what makes this work.
The perl line will write as fast as you want. The $|=1 says don't buffer the output.
I put together, after having read the answers in this question, a simple function which can also do a callback, if you need that:
function waitFor(ms, cb) {
var waitTill = new Date(new Date().getTime() + ms);
while(waitTill > new Date()){};
if (cb) {
cb()
} else {
return true
}
}
For more info on
yield sleep(2000);
you should check Redux-Saga. But it is specific to your choice of Redux as your model framework (although strictly not necessary).

Why does nodejs stop execution of while true loop inside async function

I'd like to understand better under what conditions nodejs stops a running process. My guess was that it stops the process when both the stack and the event loop are empty. But the following program only prints hello once, whereas I was expecting it to loop forever, printing hello every second.
(async () => {
while (true) {
await new Promise(resolve => setTimeout(() => console.log("hello"), 1000))
}
})();
How does the while (true) loop interact with the event loop?
You haven't mistaken how NodeJS works. Your code just has a bug: resolve never gets called.
If you change it to the following, "hello" prints forever at 1 second intervals:
(async () => {
while (true) {
await new Promise(resolve => setTimeout(() => {
console.log("hello")
resolve();
}, 1000))
}
})();
The reason your code would still end is because, in NodeJS, the resolve function falls out of scope, indicating to the V8 JS engine that the Promise can never resolve. Therefore it ends the async () => {...}, which in turn quits since it's the last function still running.
You need to call your resolve() method ,such that loop can proceed further,like below
(async() => {
while (true) {
const data = await new Promise(resolve => setTimeout(() => resolve("hello"), 1000))
console.log(data)
}
})();
You haven't resolved your promise.
Node can tell that there are no more event sources that might make something interesting happen (no more timers are scheduled) so it exits, even though there is an unresolved promise.
Compare this version, which will print hello forever, because the promise is resolved when the timeout completes and a new timeout is scheduled, thus ensuring that there is always something in the event queue, giving Node.js a reason to carry on running your program.
(async () => {
while (true) {
await new Promise(resolve => setTimeout(() => {
console.log("hello");
resolve();
}, 1000))
}
})();
If resolve is not called, then the promise is not resolved, and the await never completes and no new timeout is ever scheduled. The event queue empties and node decides that to go on doing nothing would be futile, so it exits.

Setting timeout in javascript/typescript code for cloud functions

I am currently trying to delay some actions with the cloud firestore but the setTimeout/setInterval doesn't seem to work for my code.
export const onTimerCreate = functions.firestore
.document("File/One")
.onCreate((snapshot, context) => {
setTimeout(countdown, 5000);
const messageData = snapshot.data()
const delay = countdown()
return snapshot.ref.update ({ delay : delay })
})
function countdown() {
return 0
}
I am trying to make sure the snapshot updates with a new value after that delay of 5 seconds but it just happens instantly every time... not sure what to do?
First thing's first - you can't delay a write to Firestore with Cloud Functions. I can't tell for sure if that's what you're trying to do here, but if so, you should give up that idea right now. Cloud Functions only trigger after some action has already taken place. You can't intercept a write. You can prevent a write with security rules, but security rules won't effectively allow you to delay the write. The write will happen as fast as possible.
Assuming that you do actually want to just do something in delay after the write occurs, you can use setTimeout, but be aware that you are paying for the CPU time associated with those 5 seconds.
Since background functions are required to return a promise that resolves only after all the background is complete, you need to create and arrange for that Promise to resolve after the timeout finishes, and after any other async work completes. Since you're using TypeScript (or at least you've tagged it here), async/await is the easiest way to get this done:
export const onTimerCreate = functions.firestore
.document("File/One")
.onCreate(async (snapshot, context) => {
await sleep(5000)
const messageData = snapshot.data()
await snapshot.ref.update ({ delay : delay })
})
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

Node exits without error and doesn't await promise (Event callback)

I've got a really weird issue whereby awaiting a Promise that has passed its resolve to an event-emitter callback just exits the process without error.
const {EventEmitter} = require('events');
async function main() {
console.log("entry");
let ev = new EventEmitter();
let task = new Promise(resolve=>{
ev.once("next", function(){resolve()}); console.log("added listener");
});
await task;
console.log("exit");
}
main()
.then(()=>console.log("exit"))
.catch(console.log);
process.on("uncaughtException", (e)=>console.log(e));
I'm expecting the process to halt when I run this because clearly "next" is currently never emitted. but the output I get is:
entry
added listener
and then the nodejs process terminates gracefully.
I thought it was something to do with the Garbage Collector, but ev and task are clearly still in scope on main. So I'm really at a loss as to why the process exits entirely without error.
Obviously I would eventually emit the event, but I've simplified my code to the above to reproduce. I'm on node v8.7.0. Is there something wrong with my code or is this a node bug?
This question is basically: how does node decide whether to exit the event loop or go around again?
Basically node keeps a reference count of scheduled async requests — setTimeouts, network requests, etc.. Each time one is scheduled, that count increases, and each time one is finished, the count decreases. If you arrive at the end of an event loop cycle and that reference count is zero node exits.
Simply creating a promise or event emitter does not increase the reference count — creating these objects isn't actually an async operation. For example, this promise's state will always be pending but the process exits right away:
const p = new Promise( resolve => {
if(false) resolve()
})
p.then(console.log)
In the same vein this also exits after creating the emitter and registering a listener:
const ev = new EventEmitter()
ev.on("event", (e) => console.log("event:", e))
If you expect Node to wait on an event that is never scheduled, then you may be working under the idea that Node doesn't know whether there are future events possible, but it does because it keeps a count every time one is scheduled.
So consider this small alteration:
const ev = new EventEmitter()
ev.on("event", (e) => console.log("event:", e))
const timer = setTimeout(() => ev.emit("event", "fired!"), 1000)
// ref count is not zero, event loop will go again.
// after timer fires ref count goes back to zero and node exits
As a side note, you can remove the reference to the timer with: timeout.unref(). This, unlike the previous example, will exit immediately:
const ev = new EventEmitter()
ev.on("event", (e) => console.log("event:", e))
const timer = setTimeout(() => ev.emit("event", "fired!"), 1000)
timer.unref()
There's a good talk about the event loop by Bert Belder here that clears up a lot of misconceptions: https://www.youtube.com/watch?v=PNa9OMajw9w
I was debugging for several hours why one of our scripts exits (without any errors) after one line of code in the middle of main function. It was a line await connectToDatabase(config). And you know what?
I found that difference between these two functions is CRUCIAL:
first:
async function connectToDatabase(config = {}) {
if (!config.port) return;
return new Promise(resolve => {
resolve();
})
}
second:
async function connectToDatabase(config = {}) {
return new Promise(resolve => {
if (!config.port) return;
resolve();
})
}
second function sometimes (when config.port is empty) creates never-resolved promise, it makes event loop empty, and node.js exits thinking that "nothing more to do here"
check it yourself:
// index.js - start it as node index.js
(async function main() {
console.log('STARTED')
await connectToDatabase()
console.log('CONNECTED')
console.log('DOING SOMETHING ELSE')
})()
'CONNECTED' and 'DOING SOMETHING ELSE' are NOT printed if you use second function and are printed, if you use first
As a general note, your code is combining three similar, but different methods: async/await, promises, event listeners. I'm not sure what you mean by "bombs out." But looking at the code, the result seems expected.
Your process exits, because you called promise on adding your event listener. It successfully resolves, and therefore exits. If you try to log task, it will give you undefined. Instead of logging "exit" in your then statement, log the result. Task will be undefined since the program does not wait to resolve its value and its "code block has finished".
You can simplify your code to the following. As you can see it resolves immediately since you call the resolve function.
const { EventEmitter } = require('events');
let ev = new EventEmitter()
var p = new Promise(( resolve ) => {
ev.once("next", resolve("Added Event Listener"));
})
p
.then(res => console.log(res))
.catch(e => console.log(e))

In JavaScript, does using await inside a loop block the loop?

Take the following loop:
for(var i=0; i<100; ++i){
let result = await some_slow_async_function();
do_something_with_result();
}
Does await block the loop? Or does the i continue to be incremented while awaiting?
Is the order of do_something_with_result() guaranteed sequential with regard to i? Or does it depend on how fast the awaited function is for each i?
Does await block the loop? Or does the i continue to be incremented while awaiting?
"Block" is not the right word, but yes, i does not continue to be incremented while awaiting. Instead the execution jumps back to where the async function was called, providing a promise as return value, continuing the rest of the code that follows after the function call, until the code stack has been emptied. Then when the awaiting is over, the state of the function is restored, and execution continues within that function. Whenever that function returns (completes), the corresponding promise -- that was returned earlier on -- is resolved.
Is the order of do_something_with_result() guaranteed sequential with regard to i? Or does it depend on how fast the awaited function is for each i?
The order is guaranteed. The code following the await is also guaranteed to execute only after the call stack has been emptied, i.e. at least on or after the next microtask can execute.
See how the output is in this snippet. Note especially where it says "after calling test":
async function test() {
for (let i = 0; i < 2; i++) {
console.log('Before await for ', i);
let result = await Promise.resolve(i);
console.log('After await. Value is ', result);
}
}
test().then(_ => console.log('After test() resolved'));
console.log('After calling test');
As #realbart says, it does block the loop, which then will make the calls sequential.
If you want to trigger a ton of awaitable operations and then handle them all together, you could do something like this:
const promisesToAwait = [];
for (let i = 0; i < 100; i++) {
promisesToAwait.push(fetchDataForId(i));
}
const responses = await Promise.all(promisesToAwait);
You can test async/await inside a "FOR LOOP" like this:
(async () => {
for (let i = 0; i < 100; i++) {
await delay();
console.log(i);
}
})();
function delay() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 100);
});
}
async functions return a Promise, which is an object that will eventually "resolve" to a value, or "reject" with an error. The await keyword means to wait until this value (or error) has been finalized.
So from the perspective of the running function, it blocks waiting for the result of the slow async function. The javascript engine, on the other hand, sees that this function is blocked waiting for the result, so it will go check the event loop (ie. new mouse clicks, or connection requests, etc.) to see if there are any other things it can work on until the results are returned.
Note however, that if the slow async function is slow because it is computing lots of stuff in your javascript code, the javascript engine won't have lots of resources to do other stuff (and by doing other stuff would likely make the slow async function even slower). Where the benefit of async functions really shine is for I/O intensive operations like querying a database or transmitting a large file where the javascript engine is well and truly waiting on something else (ie. database, filesystem, etc.).
The following two bits of code are functionally equivalent:
let result = await some_slow_async_function();
and
let promise = some_slow_async_function(); // start the slow async function
// you could do other stuff here while the slow async function is running
let result = await promise; // wait for the final value from the slow async function
In the second example above the slow async function is called without the await keyword, so it will start execution of the function and return a promise. Then you can do other things (if you have other things to do). Then the await keyword is used to block until the promise actually "resolves". So from the perspective of the for loop it will run synchronous.
So:
yes, the await keyword has the effect of blocking the running function until the async function either "resolves" with a value or "rejects" with an error, but it does not block the javascript engine, which can still do other things if it has other things to do while awaiting
yes, the execution of the loop will be sequential
There is an awesome tutorial about all this at http://javascript.info/async.
No Event loop isn't blocked, see example below
function sayHelloAfterSomeTime (ms) {
return new Promise((resolve, reject) => {
if (typeof ms !== 'number') return reject('ms must be a number')
setTimeout(() => {
console.log('Hello after '+ ms / 1000 + ' second(s)')
resolve()
}, ms)
})
}
async function awaitGo (ms) {
await sayHelloAfterSomeTime(ms).catch(e => console.log(e))
console.log('after awaiting for saying Hello, i can do another things ...')
}
function notAwaitGo (ms) {
sayHelloAfterSomeTime(ms).catch(e => console.log(e))
console.log('i dont wait for saying Hello ...')
}
awaitGo(1000)
notAwaitGo(1000)
console.log('coucou i am event loop and i am not blocked ...')
Here is my test solution about this interesting question:
import crypto from "crypto";
function diyCrypto() {
return new Promise((resolve, reject) => {
crypto.pbkdf2('secret', 'salt', 2000000, 64, 'sha512', (err, res) => {
if (err) {
reject(err)
return
}
resolve(res.toString("base64"))
})
})
}
setTimeout(async () => {
console.log("before await...")
const a = await diyCrypto();
console.log("after await...", a)
}, 0);
setInterval(() => {
console.log("test....")
}, 200);
Inside the setTimeout's callback the await blocks the execution. But the setInterval is keep runnning, so the Event Loop is running as usual.
Let me clarify a bit because some answers here have some wrong information about how Promise execution works, specifically when related to the event loop.
In the case of the example, await will block the loop. do_something_with_result() will not be called until await finishes it's scheduled job.
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await#handling_asyncawait_slowdown
As for the other points, Promise "jobs" run before the next event loop cycle, as microtasks. When you call Promise.then() or the resolve() function inside new Promise((resolve) => {}), you creating a Job. Both await and async are wrapper, of sorts, for Promise, that will both create a Job. Microtasks are meant to run before the next event loop cycle. That means adding a Promise Job means more work before it can move on to the next event loop cycle.
Here's an example how you can lock up your event loop because your promises (Jobs) take too long.
let tick = 0;
let time = performance.now();
setTimeout(() => console.log('Hi from timeout'), 0);
const tock = () => console.log(tick++);
const longTask = async () => {
console.log('begin task');
for(let i = 0; i < 1_000_000_000; i++) {
Math.sqrt(i);
}
console.log('done task');
}
requestAnimationFrame(()=> console.log('next frame after', performance.now() - time, 'ms'));
async function run() {
await tock();
await tock();
await longTask(); // Will stall your UI
await tock(); // Will execute even though it's already dropped frames
await tock(); // This will execute too
}
run();
// Promise.resolve().then(tock).then(tock).then(longTask).then(tock).then(tock);
In this sample, 5 total promises are created. 2 calls for tock, 1 for longTask and then 2 calls for tock. All 5 will run before the next event loop.
The execution would be:
Start JS execution
Execute normal script
Run 5 scheduled Promise jobs
End JS execution
Event Loop Cycle Start
Request Animation Frame fire
Timeout fire
The last line commented line is scheduling without async/await and results in the same.
Basically, you will stall the next event loop cycle unless you tell your JS execution where it can suspend. Your Promise jobs will continue to run in the current event loop run until it finishes its call stack. When you call something external, (like fetch), then it's likely using letting the call stack end and has a callback that will resolve the pending Promise. Like this:
function waitForClick() {
return new Promise((resolve) => {
// Use an event as a callback;
button.onclick = () => resolve();
// Let the call stack finish by implicitly not returning anything, or explicitly returning `undefined` (same thing).
// return undefined;
})
}
If you have a long job job that want to complete, either use a Web Worker to run it without pausing, or insert some pauses with something like setTimeout() or setImmediate().
Reshaping the longTask function, you can do something like this:
const longTask = async () => {
console.log('begin task');
for(let i = 0; i < 1_000_000_000; i++)
if (i && i % (10_000_000) === 0) {
await new Promise((r) => setTimeout(r,0));
}
Math.sqrt(i);
console.log('done task');
}
Basically, instead of doing 1 billion records in one shot, you only do 10 million and then wait until the next event (setTimeout) to run the next one. The bad here is it's slower because of how much you hand back to the event loop. Instead, you can use requestIdleCallback() which is better, but still not as good as multi-threading via Web Workers.
But be aware that just slapping on await or Promise.resolve().then() around a function won't help with the event loop. Both will wait until the function returns with either a Promise or a value before letting up for the event loop. You can mostly test by checking to see if the function you're calling returns an unresolved Promise immediately.
Does await block the loop? Or does the i continue to be incremented while awaiting?
No, await won't block the looping. Yes, i continues to be incremented while looping.
Is the order of do_something_with_result() guaranteed sequential with regard to i? Or does it depend on how fast the awaited function is for each i?
Order of do_something_with_result() is guaranteed sequentially but not with regards to i. It depends on how fast the awaited function runs.
All calls to some_slow_async_function() are batched, i.e., if do_something_with_result() was a console then we will see it printed the number of times the loop runs. And then sequentially, after this, all the await calls will be executed.
To better understand you can run below code snippet:
async function someFunction(){
for (let i=0;i<5;i++){
await callAPI();
console.log('After', i, 'th API call');
}
console.log("All API got executed");
}
function callAPI(){
setTimeout(()=>{
console.log("I was called at: "+new Date().getTime())}, 1000);
}
someFunction();
One can clearly see how line console.log('After', i, 'th API call'); gets printed first for entire stretch of the for loop and then at the end when all code is executed we get results from callAPI().
So if lines after await were dependent on result obtained from await calls then they will not work as expected.
To conclude, await in for-loop does not ensure successful operation on result obtained from await calls which might take some time to finish.
In node, if one uses neo-async library with waterfall, one can achieve this.

Categories

Resources