How to wait until a predicate condition becomes true in JavaScript? - javascript
I have javascript function like this:
function myFunction(number) {
var x=number;
...
... more initializations
//here need to wait until flag==true
while(flag==false)
{}
...
... do something
}
The problem is that the javascript is stuck in the while and stuck my program. so my question is how can I wait in the middle of the function until flag is true without "busy-wait"?
Javascript is single threaded, hence the page blocking behaviour. You can use the deferred/promise approach suggested by others. The most basic way would be to use window.setTimeout. E.g.
function checkFlag() {
if(flag === false) {
window.setTimeout(checkFlag, 100); /* this checks the flag every 100 milliseconds*/
} else {
/* do something*/
}
}
checkFlag();
Here is a good tutorial with further explanation: Tutorial
EDIT
As others pointed out, the best way would be to re-structure your code to use callbacks. However, this answer should give you an idea how you can 'simulate' an asynchronous behaviour with window.setTimeout.
You created an infinite loop where the flag value that terminates the loop can never be changed by code outside this loop because no code outside the loop ever gets to run - thus this will never work.
Because javascript in a browser is single threaded (except for webworkers which aren't involved here) and one thread of javascript execution runs to completion before another can run, your statement:
// this won't work!
while(flag==false) {}
will simply run forever (or until the browser complains about a non-responsive javascript loop), the page will appear to be hung and no other javascript will ever get a chance to run, thus the flag's value can never be changed by code outside this loop.
For a little more explanation, Javascript is an event driven language. That means that it runs a piece of Javascript until it returns control back to the interpreter. Then, only when it returns back to the interpreter, Javascript gets the next event from the event queue and runs it.
All things like timers and network events run through the event queue. So, when a timer fires or a network request arrives, it does not ever "interrupt" the currently running Javascript. Instead, an event gets put in the Javascript event queue and then, when the currently running Javascript finishes, the next event is pulled from the event queue and it gets its turn to run.
So, when you do an infinite loop such as while(flag==false) {}, the currently running Javascript never finishes and thus the next event is never pulled from the event queue and thus the value of flag never gets changed. They key here is that Javascript is not interrupt driven. When a timer fires, it does not interrupt the currently running Javascript, run some other Javascript and then let the currently running Javascript continue. It just gets put in the event queue waiting until the currently running Javascript is done to get its turn to run.
What you need to do is rethink how your code works and find a different way to trigger whatever code you want to run when the flag value changes. Javascript is designed as an event-driven language. So, what you need to do is figure out what events you can register an interest in so you can either listen for the event that might cause the flag to change and you can examine the flag on that event or you can trigger your own event from whatever code might change the flag or you can implement a callback function that whatever code changes that flag can call your callback whenever the piece of code responsible for changing the flag value would change it's value to true, it just calls the callback function and thus your code that wants to run when the flag gets set to true will get to run at the right time. This is much, much more efficient than trying to use some sort of timer to constantly check the flag value.
function codeThatMightChangeFlag(callback) {
// do a bunch of stuff
if (condition happens to change flag value) {
// call the callback to notify other code
callback();
}
}
Solution using Promise, async\await and EventEmitter which allows to react immediate on flag change without any kind of loops at all
const EventEmitter = require('events');
const bus = new EventEmitter();
let lock = false;
async function lockable() {
if (lock) await new Promise(resolve => bus.once('unlocked', resolve));
....
lock = true;
...some logic....
lock = false;
bus.emit('unlocked');
}
EventEmitter is builtin in node. In browser you shall need to include it by your own, for example using this package: https://www.npmjs.com/package/eventemitter3
ES6 with Async / Await ,
let meaningOfLife = false;
async function waitForMeaningOfLife(){
while (true){
if (meaningOfLife) { console.log(42); return };
await null; // prevents app from hanging
}
}
waitForMeaningOfLife();
setTimeout(()=>meaningOfLife=true,420)
Modern solution using Promise
myFunction() in the original question can be modified as follows
async function myFunction(number) {
var x=number;
...
... more initializations
await until(_ => flag == true);
...
... do something
}
where until() is this utility function
function until(conditionFunction) {
const poll = resolve => {
if(conditionFunction()) resolve();
else setTimeout(_ => poll(resolve), 400);
}
return new Promise(poll);
}
Some references to async/await and arrow functions are in a similar post:
https://stackoverflow.com/a/52652681/209794
function waitFor(condition, callback) {
if(!condition()) {
console.log('waiting');
window.setTimeout(waitFor.bind(null, condition, callback), 100); /* this checks the flag every 100 milliseconds*/
} else {
console.log('done');
callback();
}
}
Use:
waitFor(() => window.waitForMe, () => console.log('got you'))
I solved this issue by implementing the method below.
const waitUntil = (condition, checkInterval=100) => {
return new Promise(resolve => {
let interval = setInterval(() => {
if (!condition()) return;
clearInterval(interval);
resolve();
}, checkInterval)
})
}
Now, whenever you want to wait until a certain condition is met you can call it like this.
await waitUntil(() => /* your condition */)
const waitUntil = (condition, checkInterval=100) => {
return new Promise(resolve => {
let interval = setInterval(() => {
if (!condition()) return;
clearInterval(interval);
resolve();
}, checkInterval)
})
}
async function start() {
let flag = false;
console.log('wait 5 sec');
setTimeout(()=> {flag=true}, 5000); // set flag=true after 5 seconds
await waitUntil(() => flag==true ); // wait
console.log('do something when flag is true...');
}
start();
With Ecma Script 2017 You can use async-await and while together to do that
And while will not crash or lock the program even variable never be true
//First define some delay function which is called from async function
function __delay__(timer) {
return new Promise(resolve => {
timer = timer || 2000;
setTimeout(function () {
resolve();
}, timer);
});
};
//Then Declare Some Variable Global or In Scope
//Depends on you
var flag = false;
//And define what ever you want with async fuction
async function some() {
while (!flag)
await __delay__(1000);
//...code here because when Variable = true this function will
};
For iterating over ($.each) objects and executing a longish-running operation (containing nested ajax sync calls) on each object:
I first set a custom done=false property on each.
Then, in a recursive function, set each done=true and continued using setTimeout. (It's an operation meant to stop all other UI, show a progress bar and block all other use so I forgave myself for the sync calls.)
function start()
{
GlobalProducts = getproductsfromsomewhere();
$.each(GlobalProducts, function(index, product) {
product["done"] = false;
});
DoProducts();
}
function DoProducts()
{
var doneProducts = Enumerable.From(GlobalProducts).Where("$.done == true").ToArray(); //linqjs
//update progress bar here
var nextProduct = Enumerable.From(GlobalProducts).Where("$.done == false").First();
if (nextProduct) {
nextProduct.done = true;
Me.UploadProduct(nextProduct.id); //does the long-running work
setTimeout(Me.UpdateProducts, 500)
}
}
If you are allowed to use: async/await on your code, you can try this one:
const waitFor = async (condFunc: () => boolean) => {
return new Promise((resolve) => {
if (condFunc()) {
resolve();
}
else {
setTimeout(async () => {
await waitFor(condFunc);
resolve();
}, 100);
}
});
};
const myFunc = async () => {
await waitFor(() => (window as any).goahead === true);
console.log('hello world');
};
myFunc();
Demo here:
https://stackblitz.com/edit/typescript-bgtnhj?file=index.ts
On the console, just copy/paste: goahead = true.
TMCDR; (= "too much code... didn't read")
Simplest in terms of readability of the calling code and conciseness of the implementing code:
const until = (predFn) => {
const poll = (done) => (predFn() ? done() : setTimeout(() => poll(done), 500));
return new Promise(poll);
};
Example calling code:
await until(() => { myBankBalance > 1000000 });
More detailed example:
https://replit.com/#SteveChambers1/Javascript-until-function?v=1
The cleanest solution (improvement of #tdxius solution) based on controlled time interval loop, promise and timeout to reject the promise and clear intervals in case condition isn't met in a given time
const waitUntil = (condition) => {
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (!condition()) {
return;
}
clearInterval(interval);
resolve();
}, 100);
setTimeout(() => {
clearInterval(interval);
reject('your error msg');
}, 5000);
});
};
Now, whenever you want to wait until a certain condition is met, you can call it like this.
waitUntil(CONDITION_FUNCTION)
.then(() => DO_SOMETHING)
.catch((YOUR_ERROR_MSG) => console.warn(YOUR_ERROR_MSG))
using non blocking javascript with EventTarget API
In my example, i need to wait for a callback before to use it. I have no idea when this callback is set. It can be before of after i need to execute it. And i can need to call it several time (everything async)
// bus to pass event
const bus = new EventTarget();
// it's magic
const waitForCallback = new Promise((resolve, reject) => {
bus.addEventListener("initialized", (event) => {
resolve(event.detail);
});
});
// LET'S TEST IT !
// launch before callback has been set
waitForCallback.then((callback) => {
console.log(callback("world"));
});
// async init
setTimeout(() => {
const callback = (param) => { return `hello ${param.toString()}`; }
bus.dispatchEvent(new CustomEvent("initialized", {detail: callback}));
}, 500);
// launch after callback has been set
setTimeout(() => {
waitForCallback.then((callback) => {
console.log(callback("my little pony"));
});
}, 1000);
Did anyone ever think of just doing this?
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 2000);
});
}
function myFunction(number) {
var x=number;
...
... more initializations
//here need to wait until flag==true
while(flag==false)
{
await resolveAfter2Seconds();
}
...
... do something
}
I tried to used #Kiran approach like follow:
checkFlag: function() {
var currentObject = this;
if(flag == false) {
setTimeout(currentObject.checkFlag, 100);
} else {
/* do something*/
}
}
(framework that I am using force me to define functions this way).
But without success because when execution come inside checkFlag function second time, this is not my object it is Window.
So, I finished with code below
checkFlag: function() {
var worker = setInterval (function(){
if(flag == true){
/* do something*/
clearInterval (worker);
}
},100);
}
there is a node package delay very easy to use
const delay = require('delay');
(async () => {
bar();
await delay(100);
// Executed 100 milliseconds later
baz();
})();
I took an approach along the lines of the callback solutions here, but tried to make it a bit more generic. The idea is you add functions that you need to execute after something changes to a queue. When the thing happens, you then loop through the queue, call the functions and empty the queue.
Add function to queue:
let _queue = [];
const _addToQueue = (funcToQ) => {
_queue.push(funcToQ);
}
Execute and flush the queue:
const _runQueue = () => {
if (!_queue || !_queue.length) {
return;
}
_queue.forEach(queuedFunc => {
queuedFunc();
});
_queue = [];
}
And when you invoke _addToQueue you'll want to wrap the callback:
_addToQueue(() => methodYouWantToCallLater(<pass any args here like you normally would>));
When you've met the condition, call _runQueue()
This was useful for me because I had several things that needed to wait on the same condition. And it decouples the detection of the condition from whatever needs to be executed when that condition is hit.
Try avoid while loop as it could be blocking your code, use async and promises.
Just wrote this library:
https://www.npmjs.com/package/utilzed
There is a function waitForTrue
import utilzed from 'utilzed'
const checkCondition = async () => {
// anything that you are polling for to be expecting to be true
const response = await callSomeExternalApi();
return response.success;
}
// this will waitForTrue checkCondition to be true
// checkCondition will be called every 100ms
const success = await utilzed.waitForTrue(100, checkCondition, 1000);
if (success) {
// Meaning checkCondition function returns true before 1000 ms
return;
}
// meaning after 1000ms the checkCondition returns false still
// handle unsuccessful "poll for true"
Similar to Lightbeard's answer, I use the following approach
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async function until(fn) {
while (!fn()) {
await sleep(0)
}
}
async function myFunction(number) {
let x = number
...
... more initialization
await until(() => flag == true)
...
... do something
}
//function a(callback){
setTimeout(function() {
console.log('Hi I am order 1');
}, 3000);
// callback();
//}
//function b(callback){
setTimeout(function() {
console.log('Hi I am order 2');
}, 2000);
// callback();
//}
//function c(callback){
setTimeout(function() {
console.log('Hi I am order 3');
}, 1000);
// callback();
//}
/*function d(callback){
a(function(){
b(function(){
c(callback);
});
});
}
d();*/
async function funa(){
var pr1=new Promise((res,rej)=>{
setTimeout(()=>res("Hi4 I am order 1"),3000)
})
var pr2=new Promise((res,rej)=>{
setTimeout(()=>res("Hi4 I am order 2"),2000)
})
var pr3=new Promise((res,rej)=>{
setTimeout(()=>res("Hi4 I am order 3"),1000)
})
var res1 = await pr1;
var res2 = await pr2;
var res3 = await pr3;
console.log(res1,res2,res3);
console.log(res1);
console.log(res2);
console.log(res3);
}
funa();
async function f1(){
await new Promise(r=>setTimeout(r,3000))
.then(()=>console.log('Hi3 I am order 1'))
return 1;
}
async function f2(){
await new Promise(r=>setTimeout(r,2000))
.then(()=>console.log('Hi3 I am order 2'))
return 2;
}
async function f3(){
await new Promise(r=>setTimeout(r,1000))
.then(()=>console.log('Hi3 I am order 3'))
return 3;
}
async function finaloutput2(arr){
return await Promise.all([f3(),f2(),f1()]);
}
//f1().then(f2().then(f3()));
//f3().then(f2().then(f1()));
//finaloutput2();
//var pr1=new Promise(f3)
async function f(){
console.log("makesure");
var pr=new Promise((res,rej)=>{
setTimeout(function() {
console.log('Hi2 I am order 1');
}, 3000);
});
var result=await pr;
console.log(result);
}
// f();
async function g(){
console.log("makesure");
var pr=new Promise((res,rej)=>{
setTimeout(function() {
console.log('Hi2 I am order 2');
}, 2000);
});
var result=await pr;
console.log(result);
}
// g();
async function h(){
console.log("makesure");
var pr=new Promise((res,rej)=>{
setTimeout(function() {
console.log('Hi2 I am order 3');
}, 1000);
});
var result=await pr;
console.log(result);
}
async function finaloutput(arr){
return await Promise.all([f(),g(),h()]);
}
//finaloutput();
//h();
In my example, I log a new counter value every second:
var promises_arr = [];
var new_cntr_val = 0;
// fill array with promises
for (let seconds = 1; seconds < 10; seconds++) {
new_cntr_val = new_cntr_val + 5; // count to 50
promises_arr.push(new Promise(function (resolve, reject) {
// create two timeouts: one to work and one to resolve the promise
setTimeout(function(cntr) {
console.log(cntr);
}, seconds * 1000, new_cntr_val); // feed setTimeout the counter parameter
setTimeout(resolve, seconds * 1000);
}));
}
// wait for promises to finish
Promise.all(promises_arr).then(function (values) {
console.log("all promises have returned");
});
Modern and simple solution
async function waitUntil(condition, time = 100) {
while (!condition()) {
await new Promise((resolve) => setTimeout(resolve, time));
}
}
Usage
async function foo() {
await waitUntil(() => flag === true);
console.log('condition is met!');
}
Inspired by jfriend00, this worked for me
const seconds = new Date();
// wait 5 seconds for flag to become true
const waitTime = 5
const extraSeconds = seconds.setSeconds(seconds.getSeconds() + waitTime);
while (Date.now() < extraSeconds) {
// break when flag is false
if (flag === false) break;
}
Related
How to queue calls to a function with different params and each call to run function after previous call is complete
I have function slideUp() which called with a click of button, click event calls another async function removeSlideUp() which returns a promise. but clicking on the button multiple times calls the removeSlideUp() immediately, I want all the clicks to que and call removeSlideup() one after other only after 'transitionend' event is complete. I am new to javascript and do not know where I am stuck here is my code async function slideUp() { await removeSlideUp(); } function removeSlideUp() { return new Promise(function (resolve, reject) { //console.log(smartAlert.classList.contains("slideup")); if (smartAlert.classList.contains("slideup") === false) { smartAlert.classList.add("slideup"); smartAlert.addEventListener( "transitionend", function () { console.log("completed"); smartAlert.classList.remove("slideup"); resolve(); }, { once: true } ); } else { smartAlert.classList.remove("slideup"); resolve(); } }); } $('button[name="btn_show"]').click(function () { slideUp(); }); Note: It needs to be done in pure javascript no jquery or other framework I am unable to que the clicks to execute one by one when removeSlideUp is called it does produce desired results following code allowed to get promises from transitionend event properly. Code promise return code was taken from resolving a promise with EventListener from second approach for answer by #Volune //lets start playing const buttonShow = document.getElementById("btn_show"); buttonShow.addEventListener("click", async () => { let result; switch (smartAlert.classList.contains("slideup")) { case true: smartAlert.classList.remove("slideup"); //this line triger the transition to happen result = await transitoinEnd("Show alert is complete"); console.log(result); break; default: smartAlert.classList.add("slideup"); result = await transitoinEnd("Hide alert is complete"); console.log(result); setTimeout(() => { smartAlert.classList.remove("slideup"); }, 1000); result = await transitoinEnd("Show alert is complete"); console.log(result); } }); const buttonHide = document.getElementById("btn_hide"); buttonHide.addEventListener("click", async () => { smartAlert.classList.add("slideup"); //this line triger the transition to happen const result = await transitoinEnd("Hide alert is complete"); console.log(result); }); const smartAlert = document.getElementById("smart-alert"); const transitoinEnd = (message) => { return new Promise((resolve) => { smartAlert.addEventListener( "transitionend", (animationendListener = () => { smartAlert.removeEventListener( "transitionend", animationendListener ); //call any handler you want here, if needed resolve(message); }) ); }); };
I want all the clicks to que and call removeSlideup() one after other* Well yes, you need a queue then. There is none in your code currently. The simplest way to do it is just a promise chain: let queue = Promise.resolve(); $('button[name="btn_show"]').click(function () { queue = queue.then(slideUp); });
Nodejs setInterval and run first immediatly not after interval
I try to implement a loop in my noejs app that will always wait between the tasks. For this I found the setInterval function and I thought it is the solution for me. But as I found out, the first Interval, means the very first action also wait until the interval is ready. But I want that the first action runs immediatly and then each action with the given interval. In arry scope: myArray[0] starts immediatly while myArray[1..10] will start with Interval waiting time. I tried it with: function rollDice(profilearray, browserarray, url) { return new Promise((resolve, reject) => { var i = 0; const intervalId = setInterval( (function exampleFunction() { console.log(profilearray[i].profil); //######################################################################## createTestCafe("localhost", 1337, 1338, void 0, true) .then((tc) => { testcafe = tc; runner = testcafe.createRunner(); inputStore.metaUrl = url; inputStore.metaLogin = teamdataarray[0].email; inputStore.metaPassword = teamdataarray[0].password; inputStore.moderator = profilearray[i].profil; inputStore.message = profilearray[i].template; inputStore.channelid = profilearray[i].channelid; }) .then(() => { return runner .src([__basedir + "/tests/temp.js"]) .browsers(browserarray) .screenshots("", false) .run() .then((failedCount) => { testcafe.close(); if (failedCount > 0) { console.log(profilearray[i].profil); console.log("No Success. Fails: " + failedCount); //clearInterval(intervalId); //reject("Error"); } else { console.log(profilearray[i].profil); console.log("All success"); //clearInterval(intervalId); //resolve("Fertig"); } }); }) .catch((error) => { testcafe.close(); console.log(profilearray[i].profil); console.log("Testcafe Error" + error); //clearInterval(intervalId); //reject("Error"); }); //###################################################################### i++; console.log("Counter " + i); if (i === profilearray.length) { clearInterval(intervalId); resolve("Fertig"); } return exampleFunction; })(), 3000 ); //15 * 60 * 1000 max time to wait (user input) }); } The way I have done works bad because in the first action it will not start the testcafe. But in all other actions it will do. Anybody knows a better way to do this? Scope: Give a array of data and for each array start testcafe with a given waiting time. 3 seconds up to 15 minutes. Because in some cases 15 Minutes is a long time I want to start the first one without any waiting time. Iam open for any suggestion
For modern JavaScript await and async should be used instead of then and catch. This will make many things easier, and the code becomes more readable. You e.g. can use a regular for loop to iterate over an array while executing asynchronous tasks within it. And use try-catch blocks in the same way as you would in synchronous code. // a helperfunction that creates a Promise that resolves after // x milliseconds function wait(milliseconds) { return new Promise(resolve => setTimeout(resolve, milliseconds)) } async function rollDice(profilearray, browserarray, url) { for (let i = 0; i < profilearray.length; i++) { // depending how you want to handle the wait you would create // the "wait"-Promise here // let timer = wait(3000) let testcafe = await createTestCafe("localhost", 1337, 1338, void 0, true); try { let runner = testcafe.createRunner(); inputStore.metaUrl = url; inputStore.metaLogin = teamdataarray[0].email; inputStore.metaPassword = teamdataarray[0].password; inputStore.moderator = profilearray[i].profil; inputStore.message = profilearray[i].template; inputStore.channelid = profilearray[i].channelid; let failedCount = await runner.src([__basedir + "/tests/temp.js"]) .browsers(browserarray) .screenshots("", false) .run() if (failedCount > 0) { // ... } else { // ... } } catch (err) { console.log(profilearray[i].profil); console.log("Testcafe Error" + error); } finally { testcafe.close(); } // Here you would wait for the "wait"-Promise to resolve: // await timer; // This would have similar behavior to an interval. // Or you wait here for a certain amount of time. // The difference is whether you want that the time the // runner requires to run counts to the waiting time or not. await wait(3000) } return "Fertig" }
Declare function before setInterval, run setInterval(exampleFunction, time) and then run the function as usual (exampleFunction()). May not be ideal to the millisecond, but if you don't need to be perfectly precise, should work fine. Ask if you need further assistance EDIT: now looking twice at it, why are you calling the function you pass as parameter to setInterval inside the setInterval?
Rearming a promise when used with setInterval and setTimeout [duplicate]
function first(){ console.log('first') } function second(){ console.log('second') } let interval = async ()=>{ await setInterval(first,2000) await setInterval(second,2000) } interval(); Imagine that I have this code above. When I run it, first() and second() will be called at the same time; how do I call second() after first)() returns some data, for example, if first() is done, only then call second()? Because first() in my code will be working with a big amount of data and if this 2 functions will be calling at the same time, it will be hard for the server. How do I call second() each time when first() will return some data?
As mentioned above setInterval does not play well with promises if you do not stop it. In case you clear the interval you can use it like: async function waitUntil(condition) { return await new Promise(resolve => { const interval = setInterval(() => { if (condition) { resolve('foo'); clearInterval(interval); }; }, 1000); }); } Later you can use it like const bar = waitUntil(someConditionHere)
You have a few problems: Promises may only ever resolve once, setInterval() is meant to call the callback multiple times, Promises do not support this case well. Neither setInterval(), nor the more appropriate setTimeout() return Promises, therefore, awaiting on them is pointless in this context. You're looking for a function that returns a Promise which resolves after some times (using setTimeout(), probably, not setInterval()). Luckily, creating such a function is rather trivial: async function delay(ms) { // return await for better async stack trace support in case of errors. return await new Promise(resolve => setTimeout(resolve, ms)); } With this new delay function, you can implement your desired flow: function first(){ console.log('first') } function second(){ console.log('second') } let run = async ()=>{ await delay(2000); first(); await delay(2000) second(); } run();
setInterval doesn't play well with promises because it triggers a callback multiple times, while promise resolves once. It seems that it's setTimeout that fits the case. It should be promisified in order to be used with async..await: async () => { await new Promise(resolve => setTimeout(() => resolve(first()), 2000)); await new Promise(resolve => setTimeout(() => resolve(second()), 2000)); }
await expression causes async to pause until a Promise is settled so you can directly get the promise's result without await for me, I want to initiate Http request every 1s let intervalid async function testFunction() { intervalid = setInterval(() => { // I use axios like: axios.get('/user?ID=12345').then new Promise(function(resolve, reject){ resolve('something') }).then(res => { if (condition) { // do something } else { clearInterval(intervalid) } }) }, 1000) } // you can use this function like testFunction() // or stop the setInterval in any place by clearInterval(intervalid)
You could use an IFFE. This way you could escape the issue of myInterval not accepting Promise as a return type. There are cases where you need setInterval, because you want to call some function unknown amount of times with some interval in between. When I faced this problem this turned out to be the most straight-forward solution for me. I hope it help someone :) For me the use case was that I wanted to send logs to CloudWatch but try not to face the Throttle exception for sending more than 5 logs per second. So I needed to keep my logs and send them as a batch in an interval of 1 second. The solution I'm posting here is what I ended up using. async function myAsyncFunc(): Promise<string> { return new Promise<string>((resolve) => { resolve("hello world"); }); } function myInterval(): void { setInterval(() => { void (async () => { await myAsyncFunc(); })(); }, 5_000); } // then call like so myInterval();
Looked through all the answers but still didn't find the correct one that would work exactly how the OP is asked. This is what I used for the same purpose: async function waitInterval(callback, ms) { return new Promise(resolve => { let iteration = 0; const interval = setInterval(async () => { if (await callback(iteration, interval)) { resolve(); clearInterval(interval); } iteration++; }, ms); }); } function first(i) { console.log(`first: ${i}`); // If the condition below is true the timer finishes return i === 5; } function second(i) { console.log(`second: ${i}`); // If the condition below is true the timer finishes return i === 5; } (async () => { console.log('start'); await waitInterval(first, 1000); await waitInterval(second, 1000); console.log('finish'); })() In my example, I also put interval iteration count and the timer itself, just in case the caller would need to do something with it. However, it's not necessary
In my case, I needed to iterate through a list of images, pausing in between each, and then a longer pause at the end before re-looping through. I accomplished this by combining several techniques from above, calling my function recursively and awaiting a timeout. If at any point another trigger changes my animationPaused:boolean, my recursive function will exit. const loopThroughImages = async() => { for (let i=0; i<numberOfImages; i++){ if (animationPaused) { return; } this.updateImage(i); await timeout(700); } await timeout(1000); loopThroughImages(); } loopThroughImages();
Async/await do not make the promises synchronous. To my knowledge, it's just a different syntax for return Promise and .then(). Here i rewrote the async function and left both versions, so you can see what it really does and compare. It's in fact a cascade of Promises. // by the way no need for async there. the callback does not return a promise, so no need for await. function waitInterval(callback, ms) { return new Promise(resolve => { let iteration = 0; const interval = setInterval(async () => { if (callback(iteration, interval)) { resolve(); clearInterval(interval); } iteration++; }, ms); }); } function first(i) { console.log(`first: ${i}`); // If the condition below is true the timer finishes return i === 5; } function second(i) { console.log(`second: ${i}`); // If the condition below is true the timer finishes return i === 5; } // async function with async/await, this code ... (async () => { console.log('start'); await waitInterval(first, 1000); await waitInterval(second, 1000); console.log('finish'); })() //... returns a pending Promise and ... console.log('i do not wait'); // ... is kinda identical to this code. // still asynchronous but return Promise statements with then cascade. (() => { console.log('start again'); return waitInterval(first, 1000).then(() => { return waitInterval(second, 1000).then(() => { console.log('finish again'); }); }); })(); // returns a pending Promise... console.log('i do not wait either'); You can see the two async functions both execute at the same time. So using promises around intervals here is not very useful, as it's still just intervals, and promises changes nothing, and make things confusing... As the code is calling callbacks repeatedly into an interval, this is, i think, a cleaner way: function first(i) { console.log(`first: ${i}`); // If the condition below is true the timer finishes return i === 5; } function second(i) { console.log(`second: ${i}`); // If the condition below is true the timer finishes return i === 5; } function executeThroughTime(...callbacks){ console.log('start'); let callbackIndex = 0; // to track current callback. let timerIndex = 0; // index given to callbacks let interval = setInterval(() =>{ if (callbacks[callbackIndex](timerIndex++)){ // callback return true when it finishes. timerIndex = 0; // resets for next callback if (++callbackIndex>=callbacks.length){ // if no next callback finish. clearInterval(interval); console.log('finish'); } } },1000) } executeThroughTime(first,second); console.log('and i still do not wait ;)'); Also, this solution execute a callback every secondes. if the callbacks are async requests that takes more than one sec to resolve, and i can't afford for them to overlap, then, instead of doing iterative call with repetitive interval, i would get the request resolution to call the next request (through a timer if i don't want to harass the server). Here the "recursive" task is called lTask, does pretty much the same as before, except that, as i do not have an interval anymore, i need a new timer each iteration. // slow internet request simulation. with a Promise, could be a callback. function simulateAsync1(i) { console.log(`first pending: ${i}`); return new Promise((resolve) =>{ setTimeout(() => resolve('got that first big data'), Math.floor(Math.random()*1000)+ 1000);//simulate request that last between 1 and 2 sec. }).then((result) =>{ console.log(`first solved: ${i} ->`, result); return i==2; }); } // slow internet request simulation. with a Promise, could be a callback. function simulateAsync2(i) { console.log(`second pending: ${i}`); return new Promise((resolve) =>{ setTimeout(() => resolve('got that second big data'), Math.floor(Math.random()*1000) + 1000);//simulate request that last between 1 and 2 sec. }).then((result) =>{ // promise is resolved console.log(`second solved: ${i} ->`,result); return i==4; // return a promise }); } function executeThroughTime(...asyncCallbacks){ console.log('start'); let callbackIndex = 0; let timerIndex = 0; let lPreviousTime = Date.now(); let lTask = () => { // timeout callback. asyncCallbacks[callbackIndex](timerIndex++).then((result) => { // the setTimeout for the next task is set when the promise is solved. console.log('result',result) if (result) { // current callback is done. timerIndex = 0; if (++callbackIndex>=asyncCallbacks.length){//are all callbacks done ? console.log('finish'); return;// its over } } console.log('time elapsed since previous call',Date.now() - lPreviousTime); lPreviousTime = Date.now(); //console.log('"wait" 1 sec (but not realy)'); setTimeout(lTask,1000);//redo task after 1 sec. //console.log('i do not wait'); }); } lTask();// no need to set a timer for first call. } executeThroughTime(simulateAsync1,simulateAsync2); console.log('i do not wait'); Next step would be to empty a fifo with the interval, and fill it with web request promises...
Is there a better implementation for this async loop in JS?
So I'm writing a node.js script that uses Playwright to send different messages at a set interval in a browser. I'm running into an issue with an asynchronous part of the script. I initially had it set up in this way async start() { this.interval = setInterval(async () => { await sendMessages(this.messages); }, 1000); } I'm pretty sure the await keyword does nothing here and just executes the next iteration after 1000 ms regardless of whether sendMessages is finished executing or not. This was resulting in messages being double sent when they hit their interval requirement for sending again. This code works, and is the subject of my question: async start() { while (!this.stopFlag) { await sendMessages(this.messages); await timeout(1000); } } timeout is a simple function that resolves a Promise after a setTimeout call. However, this seems kind of hacky and I don't really like this way of implementation. Does anyone have a more clever way to execute something like this? Here are some more code details: Messenger.js import Message from './Message.js' import { timeout } from './Timeout.js' export default class Messenger { constructor(messages, page) { this.page = page; this.messages = messages.map((msg) => new Message(msg)); this.stopFlag = false; } async start() { while (!this.stopFlag) { await this.sendMessages(); await timeout(1000); } } async sendMessages() { for (const message of this.messages) { if (message.shouldSend()) { await message.send(this.page); } } } stop() { this.stopFlag = true; } } Message.js export default class Message { constructor(message) { this.text = message.text; this.timing = message.timing; this.lastSent = null; } async send(page) { logMessage(`Sending message "${this.text}" in channel.`); await playwrightFunctionToSendMessage(); this.lastSent = new Date(); } shouldSend() { // returns a boolean based on this.timing and this.lastSent } }
await inside setInterval will not work as expected and will keep adding to the calls to the queue and move on. this.interval = setInterval(async () => { await sendMessages(this.messages); // will not be async as expected }, 1000); if there is a possibility that your logic could take longer to execute than the interval time, it is recommended that you recursively call a named function using setTimeout(). For example, if using setInterval() to poll a remote server every 5 seconds, network latency, an unresponsive server, and a host of other issues could prevent the request from completing in its allotted time. As such, you may find yourself with queued up XHR requests that won't necessarily return in order. Challenge is if you need long-running processes to execute at set times and need a result before the next execution time. Obviously, if it takes longer than the interval to process then you will need a more advanced way to keep track of batches and or reduce the workload. But if you simply want a way to wait until completion before executing the process again after a delay, you could do: function sendMessages() { return new Promise((resolve) => { setTimeout(() => { console.info('done sending'); return resolve(); }, 2000); }) } let stopFlag = false; async function process() { if (stopFlag) { console.info('stopping'); } else { // wait till complete and set a new timeout await sendMessages(); setTimeout(() => process(), 1000); } } process(); // trigger a stop setTimeout(() => { stopFlag = true; }, 5000);
Instead of the stop method reassigning this.stopFlag, have it clear the interval or timeout. async start() { if (this.timeoutId) return; // timeout is already running - don't start another const startTimeout = () => { this.timeoutId = setTimeout(() => { this.sendMessages() .then(() => { if (this.timeoutId) startTimeout(); }) .catch(handleErrors); // don't forget this part }, 1000); // 1 second between finish of last sendMessages and start of next }; startTimeout(); } stop() { clearTimeout(this.timeoutId); this.timeoutId = null; }
How to delay execution of a function in Javascript [duplicate]
I have javascript function like this: function myFunction(number) { var x=number; ... ... more initializations //here need to wait until flag==true while(flag==false) {} ... ... do something } The problem is that the javascript is stuck in the while and stuck my program. so my question is how can I wait in the middle of the function until flag is true without "busy-wait"?
Javascript is single threaded, hence the page blocking behaviour. You can use the deferred/promise approach suggested by others. The most basic way would be to use window.setTimeout. E.g. function checkFlag() { if(flag === false) { window.setTimeout(checkFlag, 100); /* this checks the flag every 100 milliseconds*/ } else { /* do something*/ } } checkFlag(); Here is a good tutorial with further explanation: Tutorial EDIT As others pointed out, the best way would be to re-structure your code to use callbacks. However, this answer should give you an idea how you can 'simulate' an asynchronous behaviour with window.setTimeout.
You created an infinite loop where the flag value that terminates the loop can never be changed by code outside this loop because no code outside the loop ever gets to run - thus this will never work. Because javascript in a browser is single threaded (except for webworkers which aren't involved here) and one thread of javascript execution runs to completion before another can run, your statement: // this won't work! while(flag==false) {} will simply run forever (or until the browser complains about a non-responsive javascript loop), the page will appear to be hung and no other javascript will ever get a chance to run, thus the flag's value can never be changed by code outside this loop. For a little more explanation, Javascript is an event driven language. That means that it runs a piece of Javascript until it returns control back to the interpreter. Then, only when it returns back to the interpreter, Javascript gets the next event from the event queue and runs it. All things like timers and network events run through the event queue. So, when a timer fires or a network request arrives, it does not ever "interrupt" the currently running Javascript. Instead, an event gets put in the Javascript event queue and then, when the currently running Javascript finishes, the next event is pulled from the event queue and it gets its turn to run. So, when you do an infinite loop such as while(flag==false) {}, the currently running Javascript never finishes and thus the next event is never pulled from the event queue and thus the value of flag never gets changed. They key here is that Javascript is not interrupt driven. When a timer fires, it does not interrupt the currently running Javascript, run some other Javascript and then let the currently running Javascript continue. It just gets put in the event queue waiting until the currently running Javascript is done to get its turn to run. What you need to do is rethink how your code works and find a different way to trigger whatever code you want to run when the flag value changes. Javascript is designed as an event-driven language. So, what you need to do is figure out what events you can register an interest in so you can either listen for the event that might cause the flag to change and you can examine the flag on that event or you can trigger your own event from whatever code might change the flag or you can implement a callback function that whatever code changes that flag can call your callback whenever the piece of code responsible for changing the flag value would change it's value to true, it just calls the callback function and thus your code that wants to run when the flag gets set to true will get to run at the right time. This is much, much more efficient than trying to use some sort of timer to constantly check the flag value. function codeThatMightChangeFlag(callback) { // do a bunch of stuff if (condition happens to change flag value) { // call the callback to notify other code callback(); } }
Solution using Promise, async\await and EventEmitter which allows to react immediate on flag change without any kind of loops at all const EventEmitter = require('events'); const bus = new EventEmitter(); let lock = false; async function lockable() { if (lock) await new Promise(resolve => bus.once('unlocked', resolve)); .... lock = true; ...some logic.... lock = false; bus.emit('unlocked'); } EventEmitter is builtin in node. In browser you shall need to include it by your own, for example using this package: https://www.npmjs.com/package/eventemitter3
ES6 with Async / Await , let meaningOfLife = false; async function waitForMeaningOfLife(){ while (true){ if (meaningOfLife) { console.log(42); return }; await null; // prevents app from hanging } } waitForMeaningOfLife(); setTimeout(()=>meaningOfLife=true,420)
Modern solution using Promise myFunction() in the original question can be modified as follows async function myFunction(number) { var x=number; ... ... more initializations await until(_ => flag == true); ... ... do something } where until() is this utility function function until(conditionFunction) { const poll = resolve => { if(conditionFunction()) resolve(); else setTimeout(_ => poll(resolve), 400); } return new Promise(poll); } Some references to async/await and arrow functions are in a similar post: https://stackoverflow.com/a/52652681/209794
function waitFor(condition, callback) { if(!condition()) { console.log('waiting'); window.setTimeout(waitFor.bind(null, condition, callback), 100); /* this checks the flag every 100 milliseconds*/ } else { console.log('done'); callback(); } } Use: waitFor(() => window.waitForMe, () => console.log('got you'))
I solved this issue by implementing the method below. const waitUntil = (condition, checkInterval=100) => { return new Promise(resolve => { let interval = setInterval(() => { if (!condition()) return; clearInterval(interval); resolve(); }, checkInterval) }) } Now, whenever you want to wait until a certain condition is met you can call it like this. await waitUntil(() => /* your condition */) const waitUntil = (condition, checkInterval=100) => { return new Promise(resolve => { let interval = setInterval(() => { if (!condition()) return; clearInterval(interval); resolve(); }, checkInterval) }) } async function start() { let flag = false; console.log('wait 5 sec'); setTimeout(()=> {flag=true}, 5000); // set flag=true after 5 seconds await waitUntil(() => flag==true ); // wait console.log('do something when flag is true...'); } start();
With Ecma Script 2017 You can use async-await and while together to do that And while will not crash or lock the program even variable never be true //First define some delay function which is called from async function function __delay__(timer) { return new Promise(resolve => { timer = timer || 2000; setTimeout(function () { resolve(); }, timer); }); }; //Then Declare Some Variable Global or In Scope //Depends on you var flag = false; //And define what ever you want with async fuction async function some() { while (!flag) await __delay__(1000); //...code here because when Variable = true this function will };
For iterating over ($.each) objects and executing a longish-running operation (containing nested ajax sync calls) on each object: I first set a custom done=false property on each. Then, in a recursive function, set each done=true and continued using setTimeout. (It's an operation meant to stop all other UI, show a progress bar and block all other use so I forgave myself for the sync calls.) function start() { GlobalProducts = getproductsfromsomewhere(); $.each(GlobalProducts, function(index, product) { product["done"] = false; }); DoProducts(); } function DoProducts() { var doneProducts = Enumerable.From(GlobalProducts).Where("$.done == true").ToArray(); //linqjs //update progress bar here var nextProduct = Enumerable.From(GlobalProducts).Where("$.done == false").First(); if (nextProduct) { nextProduct.done = true; Me.UploadProduct(nextProduct.id); //does the long-running work setTimeout(Me.UpdateProducts, 500) } }
If you are allowed to use: async/await on your code, you can try this one: const waitFor = async (condFunc: () => boolean) => { return new Promise((resolve) => { if (condFunc()) { resolve(); } else { setTimeout(async () => { await waitFor(condFunc); resolve(); }, 100); } }); }; const myFunc = async () => { await waitFor(() => (window as any).goahead === true); console.log('hello world'); }; myFunc(); Demo here: https://stackblitz.com/edit/typescript-bgtnhj?file=index.ts On the console, just copy/paste: goahead = true.
TMCDR; (= "too much code... didn't read") Simplest in terms of readability of the calling code and conciseness of the implementing code: const until = (predFn) => { const poll = (done) => (predFn() ? done() : setTimeout(() => poll(done), 500)); return new Promise(poll); }; Example calling code: await until(() => { myBankBalance > 1000000 }); More detailed example: https://replit.com/#SteveChambers1/Javascript-until-function?v=1
The cleanest solution (improvement of #tdxius solution) based on controlled time interval loop, promise and timeout to reject the promise and clear intervals in case condition isn't met in a given time const waitUntil = (condition) => { return new Promise((resolve, reject) => { const interval = setInterval(() => { if (!condition()) { return; } clearInterval(interval); resolve(); }, 100); setTimeout(() => { clearInterval(interval); reject('your error msg'); }, 5000); }); }; Now, whenever you want to wait until a certain condition is met, you can call it like this. waitUntil(CONDITION_FUNCTION) .then(() => DO_SOMETHING) .catch((YOUR_ERROR_MSG) => console.warn(YOUR_ERROR_MSG))
using non blocking javascript with EventTarget API In my example, i need to wait for a callback before to use it. I have no idea when this callback is set. It can be before of after i need to execute it. And i can need to call it several time (everything async) // bus to pass event const bus = new EventTarget(); // it's magic const waitForCallback = new Promise((resolve, reject) => { bus.addEventListener("initialized", (event) => { resolve(event.detail); }); }); // LET'S TEST IT ! // launch before callback has been set waitForCallback.then((callback) => { console.log(callback("world")); }); // async init setTimeout(() => { const callback = (param) => { return `hello ${param.toString()}`; } bus.dispatchEvent(new CustomEvent("initialized", {detail: callback})); }, 500); // launch after callback has been set setTimeout(() => { waitForCallback.then((callback) => { console.log(callback("my little pony")); }); }, 1000);
Did anyone ever think of just doing this? function resolveAfter2Seconds() { return new Promise(resolve => { setTimeout(() => { resolve(); }, 2000); }); } function myFunction(number) { var x=number; ... ... more initializations //here need to wait until flag==true while(flag==false) { await resolveAfter2Seconds(); } ... ... do something }
I tried to used #Kiran approach like follow: checkFlag: function() { var currentObject = this; if(flag == false) { setTimeout(currentObject.checkFlag, 100); } else { /* do something*/ } } (framework that I am using force me to define functions this way). But without success because when execution come inside checkFlag function second time, this is not my object it is Window. So, I finished with code below checkFlag: function() { var worker = setInterval (function(){ if(flag == true){ /* do something*/ clearInterval (worker); } },100); }
there is a node package delay very easy to use const delay = require('delay'); (async () => { bar(); await delay(100); // Executed 100 milliseconds later baz(); })();
I took an approach along the lines of the callback solutions here, but tried to make it a bit more generic. The idea is you add functions that you need to execute after something changes to a queue. When the thing happens, you then loop through the queue, call the functions and empty the queue. Add function to queue: let _queue = []; const _addToQueue = (funcToQ) => { _queue.push(funcToQ); } Execute and flush the queue: const _runQueue = () => { if (!_queue || !_queue.length) { return; } _queue.forEach(queuedFunc => { queuedFunc(); }); _queue = []; } And when you invoke _addToQueue you'll want to wrap the callback: _addToQueue(() => methodYouWantToCallLater(<pass any args here like you normally would>)); When you've met the condition, call _runQueue() This was useful for me because I had several things that needed to wait on the same condition. And it decouples the detection of the condition from whatever needs to be executed when that condition is hit.
Try avoid while loop as it could be blocking your code, use async and promises. Just wrote this library: https://www.npmjs.com/package/utilzed There is a function waitForTrue import utilzed from 'utilzed' const checkCondition = async () => { // anything that you are polling for to be expecting to be true const response = await callSomeExternalApi(); return response.success; } // this will waitForTrue checkCondition to be true // checkCondition will be called every 100ms const success = await utilzed.waitForTrue(100, checkCondition, 1000); if (success) { // Meaning checkCondition function returns true before 1000 ms return; } // meaning after 1000ms the checkCondition returns false still // handle unsuccessful "poll for true"
Similar to Lightbeard's answer, I use the following approach function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)) } async function until(fn) { while (!fn()) { await sleep(0) } } async function myFunction(number) { let x = number ... ... more initialization await until(() => flag == true) ... ... do something }
//function a(callback){ setTimeout(function() { console.log('Hi I am order 1'); }, 3000); // callback(); //} //function b(callback){ setTimeout(function() { console.log('Hi I am order 2'); }, 2000); // callback(); //} //function c(callback){ setTimeout(function() { console.log('Hi I am order 3'); }, 1000); // callback(); //} /*function d(callback){ a(function(){ b(function(){ c(callback); }); }); } d();*/ async function funa(){ var pr1=new Promise((res,rej)=>{ setTimeout(()=>res("Hi4 I am order 1"),3000) }) var pr2=new Promise((res,rej)=>{ setTimeout(()=>res("Hi4 I am order 2"),2000) }) var pr3=new Promise((res,rej)=>{ setTimeout(()=>res("Hi4 I am order 3"),1000) }) var res1 = await pr1; var res2 = await pr2; var res3 = await pr3; console.log(res1,res2,res3); console.log(res1); console.log(res2); console.log(res3); } funa(); async function f1(){ await new Promise(r=>setTimeout(r,3000)) .then(()=>console.log('Hi3 I am order 1')) return 1; } async function f2(){ await new Promise(r=>setTimeout(r,2000)) .then(()=>console.log('Hi3 I am order 2')) return 2; } async function f3(){ await new Promise(r=>setTimeout(r,1000)) .then(()=>console.log('Hi3 I am order 3')) return 3; } async function finaloutput2(arr){ return await Promise.all([f3(),f2(),f1()]); } //f1().then(f2().then(f3())); //f3().then(f2().then(f1())); //finaloutput2(); //var pr1=new Promise(f3) async function f(){ console.log("makesure"); var pr=new Promise((res,rej)=>{ setTimeout(function() { console.log('Hi2 I am order 1'); }, 3000); }); var result=await pr; console.log(result); } // f(); async function g(){ console.log("makesure"); var pr=new Promise((res,rej)=>{ setTimeout(function() { console.log('Hi2 I am order 2'); }, 2000); }); var result=await pr; console.log(result); } // g(); async function h(){ console.log("makesure"); var pr=new Promise((res,rej)=>{ setTimeout(function() { console.log('Hi2 I am order 3'); }, 1000); }); var result=await pr; console.log(result); } async function finaloutput(arr){ return await Promise.all([f(),g(),h()]); } //finaloutput(); //h();
In my example, I log a new counter value every second: var promises_arr = []; var new_cntr_val = 0; // fill array with promises for (let seconds = 1; seconds < 10; seconds++) { new_cntr_val = new_cntr_val + 5; // count to 50 promises_arr.push(new Promise(function (resolve, reject) { // create two timeouts: one to work and one to resolve the promise setTimeout(function(cntr) { console.log(cntr); }, seconds * 1000, new_cntr_val); // feed setTimeout the counter parameter setTimeout(resolve, seconds * 1000); })); } // wait for promises to finish Promise.all(promises_arr).then(function (values) { console.log("all promises have returned"); });
Modern and simple solution async function waitUntil(condition, time = 100) { while (!condition()) { await new Promise((resolve) => setTimeout(resolve, time)); } } Usage async function foo() { await waitUntil(() => flag === true); console.log('condition is met!'); }
Inspired by jfriend00, this worked for me const seconds = new Date(); // wait 5 seconds for flag to become true const waitTime = 5 const extraSeconds = seconds.setSeconds(seconds.getSeconds() + waitTime); while (Date.now() < extraSeconds) { // break when flag is false if (flag === false) break; }