Related
I am not an experienced Javascript programmer but I find it very difficult to execute 2 time-consuming functions in the row.
I have spent some days on studying promises or async/await but not success so far.
console.log("synch 1");
slowFunction1();
slowFunction2();
console.log("synch 2");
Most examples describe get json functions,setTimeOut etc. But in my case I have to do heavy mathematical calculations and the sequential execution is required. How could I do this?
Depends on what you're trying to achieve and what the environment is. I will assume the slow function is blocking and you're going for the main thread not to be blocked during execution. Assuming you are talking about:
Web:
Web workers would be your best bet (https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers)
Some example code would be:
var myWorker = new Worker('file-containing-slow-code.js');
myWorker.postMessage({execute: 'slowFunction1'});
myWorker.onmessage((msg) => {
const {result, functionExecuted} = msg.data;
console.log(result);
});
//contents of file-containing-slow-code.js
onmessage = function(e) {
var result = funcs[e.data.execute]();
postMessage({result, functionExecuted: e.data.execute});
}
const funcs = {
slowFunction1: () => {
// do slow stuff;
}
};
Node.js
Using child processes (https://nodejs.org/api/child_process.html#child_process_child_process)
You could wrap your functions in a promise like this, and only resolve when you want the code to continue:
function slowFunction1() {
return new Promise(resolve => {
setTimeout(() => resolve(), 1000)
})
}
function slowFunction2() {
return new Promise(resolve => {
setTimeout(() => resolve(), 1000)
})
}
console.log("synch 1");
slowFunction1().then(() => {
slowFunction2().then(() => {
console.log("synch 2");
});
})
This code should wait 1 second for each function before it console.log()'s "synch 2"
OR, you could use async/await instead of using .then(), like this:
function slowFunction1() {
return new Promise(resolve => {
setTimeout(() => resolve(), 1000)
})
}
function slowFunction2() {
return new Promise(resolve => {
setTimeout(() => resolve(), 1000)
})
}
async function main() {
console.log("synch 1");
await slowFunction1()
await slowFunction2()
console.log("synch 2");
}
main()
You got this right,you are trying to achieve a asychronous task.
Asynchronous Task
A task which will take some time to complete.
Way 1 - Callbacks
You need to give a callback to your further task.
Let me give you an example.
function myHugeTask(callback) {
// I need to do my task
console.log('Hey I am first task')
callback()
}
const someTaskWhichINeedToExecuteLater = () => {
console.log('Hey I am after the first task')
}
myHugeTask(someTaskWhichINeedToExecuteLater)
Way 2 - Promises
Promises is like a real world promise. If I promised you then I will either complete it or reject it.
Let see
const myHugeTaskPromise = new Promise(resolve => {
// Do my huge task no matter how much time it takes
console.log('I will complete first')
resolve()
})
myHugeTaskPromise.then(()=>{
console.log('Need to done when my promise get fulfilled')
})
Dear Folks that are smarter than me (everyone). I'm trying to use NodeJS to pull AWS SQS every 20 seconds (long pulling). Once it does pull a message the function will transcode a video that can take up to 5 minutes to complete.
The problem: I don't want to pull AWS SQS if I'm still processing a video. So, I need my setInterval function to skip the aws.SQS.recieveMessage function IF my promise is not resolved. Anyone know a good way to do this?
Here is my horrible test to see if I can get the concepts down (does not work):
// Quick Testbed
let asyncthingy = asyncCall();
const testTimer = () => {
if (!asyncthingy.promise){
asyncthingy = asyncCall();
}
console.log("I executed the specified function");
}
function resolveAfter4Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
},4000);
});
}
async function asyncCall() {
console.log("I'm the async call");
let result = await resolveAfter4Seconds();
}
setInterval(testTimer, 1500);
After some playing around and giving up with the idea of a native way to check the promise status, I ended up setting the status manually. If anyone can find a better way of doing this please share. Below is the cleaned up test code that should also be much more clear.
// Quick Testbed
var transcoderstatus = 0;
async function startSQSListener() {
if (!transcoderstatus){
transcoderstatus = 1;
let result = await checkSQSQueue();
transcodeVideo();
}
//console.log("Transcoder Status: ",transcoderstatus);
}
function checkSQSQueue() {
console.log("Checking SQS Queue");
return new Promise(resolve => {
setTimeout(() => {
console.log("Pulled new message");
resolve('resolved');
},2000);
});
}
function transcodeVideo() {
transcoderstatus = 1;
console.log("Transcoding Video");
return new Promise(resolve => {
setTimeout(() => {
transcoderstatus = 0;
console.log("Transcoding completed");
console.log("---------------");
resolve('resolved');
},10000);
});
}
setInterval(startSQSListener, 1500);
I am trying to scrape some links with headless-chrome/puppeteer while scrolling down like this:
let interval
const linkScraper = async () => {
return new Promise(async (resolve,reject) => {
interval = setInterval(async () => {
const visiblePosts = await page.$$("div[class*='wrapper']")
const data = await handleVisiblePosts(visiblePosts)
allPosts = {...allPosts, ...data}
await scroll()
const stop = await areWeAtTheBottom()
if (stop) {
console.log('STOPPING')
clearInterval(interval)
resolve()
}
}, 100);
})
}
problem? clearInterval doesn't actually stop the interval. stopping gets printed multiple times.
I suspect it could also be because setinterval is async, which it needs to be in order to use await.
I can find the following possible reasons why your interval would not get stopped:
You are never getting to the stop condition.
You are overwriting the interval variable somehow so the actual interval you want to stop is no longer saved.
You are getting a rejected promise.
There does not appear to be any reason why the interval variable needs to be outside the linkScraper function and putting it inside the function will prevent it from getting overwritten in any way.
With this many await calls, it seems wise to add a try/catch to catch any rejected promises and stop the interval if there's an error.
If you see the STOPPING being logged, then you are apparently hitting the stop condition so it appears it would have to be an overwritten interval variable.
Here's a version that cannot overwrite the interval variable and makes a few other changes for code cleanliness:
const linkScraper = async () => {
return new Promise((resolve, reject) => {
const interval = setInterval(async () => {
try {
const visiblePosts = await page.$$("div[class*='wrapper']");
const data = await handleVisiblePosts(visiblePosts);
allPosts = { ...allPosts, ...data};
await scroll();
const stop = await areWeAtTheBottom();
if (stop) {
console.log('STOPPING');
clearInterval(interval);
resolve();
}
} catch(e) {
clearInterval(interval);
reject(e);
}
}, 100);
});
}
In cleaning up this code, I ran into a couple questions:
Do all four of your functions that you use await with actually return a promise?
And, where is allPosts declared?
EDIT: Just discovered another issue. setInterval() isn't aware of the await calls inside your function. Remember, the outer function doesn't actually block. It returns immediately as soon as you hit an await. That means that you can get another setInterval() callback while you were still processing the async operations of the first one. That will mess things up. Here's a way around that:
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
const linkScraper = () => {
console.log("starting linkScraper");
async function run() {
const visiblePosts = await page.$$("div[class*='wrapper']");
const data = await handleVisiblePosts(visiblePosts);
allPosts = { ...allPosts, ...data};
await scroll();
const stop = await areWeAtTheBottom();
if (stop) {
console.log('STOPPING');
return "stop";
}
return "continue";
}
return run().then(result => {
if (result === "continue") {
return delay(100).then(run);
}
})
});
}
I accepted jfriend00's solution as it pointed me in the right direction, my slightly modified, final and working version looks like this:
const linkScraper = async () => {
return new Promise(async (resolve, reject) => {
const run = async () => {
console.log("running")
const visiblePosts = await page.$$("div[class*='wrapper']");
const data = await handleVisiblePosts(visiblePosts);
allPosts = {...allPosts, ...data};
await scroll();
const stop = await areWeAtTheBottom();
if (stop) {
console.log('STOPPING');
resolve()
} else {
await page.waitFor(100)
await run()
}
}
await run()
})
}
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;
}
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;
}