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"?
This question is related to
javascript
synchronization
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_x000D_
function __delay__(timer) {_x000D_
return new Promise(resolve => {_x000D_
timer = timer || 2000;_x000D_
setTimeout(function () {_x000D_
resolve();_x000D_
}, timer);_x000D_
});_x000D_
};_x000D_
_x000D_
//Then Declare Some Variable Global or In Scope_x000D_
//Depends on you_x000D_
var flag = false;_x000D_
_x000D_
//And define what ever you want with async fuction_x000D_
async function some() {_x000D_
while (!flag)_x000D_
await __delay__(1000);_x000D_
_x000D_
//...code here because when Variable = true this function will_x000D_
};
_x000D_
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)
}
}
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'))
In my example, I log a new counter value every second:
var promises_arr = [];_x000D_
var new_cntr_val = 0;_x000D_
_x000D_
// fill array with promises_x000D_
for (let seconds = 1; seconds < 10; seconds++) {_x000D_
new_cntr_val = new_cntr_val + 5; // count to 50_x000D_
promises_arr.push(new Promise(function (resolve, reject) {_x000D_
// create two timeouts: one to work and one to resolve the promise_x000D_
setTimeout(function(cntr) {_x000D_
console.log(cntr);_x000D_
}, seconds * 1000, new_cntr_val); // feed setTimeout the counter parameter_x000D_
setTimeout(resolve, seconds * 1000);_x000D_
}));_x000D_
}_x000D_
_x000D_
// wait for promises to finish_x000D_
Promise.all(promises_arr).then(function (values) {_x000D_
console.log("all promises have returned");_x000D_
});
_x000D_
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);
}
Javascript is single threaded, hence the page blocking behaviour. You can use the deferred/promise approach suggested by others, but 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
.
I solved this issue by implementing the method below.
const waitUntil = (condition) => {
return new Promise((resolve) => {
let interval = setInterval(() => {
if (!condition()) {
return
}
clearInterval(interval)
resolve()
}, 100)
})
}
Now, whenever you want to wait until a certain condition is met you can call it like this.
await waitUntil(() => /* your condition */)
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
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
there is a node package delay
very easy to use
const delay = require('delay');
(async () => {
bar();
await delay(100);
// Executed 100 milliseconds later
baz();
})();
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)
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_x000D_
const bus = new EventTarget();_x000D_
_x000D_
// it's magic_x000D_
const waitForCallback = new Promise((resolve, reject) => {_x000D_
bus.addEventListener("initialized", (event) => {_x000D_
resolve(event.detail);_x000D_
});_x000D_
});_x000D_
_x000D_
_x000D_
_x000D_
// LET'S TEST IT !_x000D_
_x000D_
_x000D_
// launch before callback has been set_x000D_
waitForCallback.then((callback) => {_x000D_
console.log(callback("world"));_x000D_
});_x000D_
_x000D_
_x000D_
// async init_x000D_
setTimeout(() => {_x000D_
const callback = (param) => { return `hello ${param.toString()}`; }_x000D_
bus.dispatchEvent(new CustomEvent("initialized", {detail: callback}));_x000D_
}, 500);_x000D_
_x000D_
_x000D_
// launch after callback has been set_x000D_
setTimeout(() => {_x000D_
waitForCallback.then((callback) => {_x000D_
console.log(callback("my little pony"));_x000D_
});_x000D_
}, 1000);
_x000D_
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.
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
}
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;
}
_x000D_
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"
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
.
//function a(callback){_x000D_
setTimeout(function() {_x000D_
console.log('Hi I am order 1');_x000D_
}, 3000);_x000D_
// callback();_x000D_
//}_x000D_
_x000D_
//function b(callback){_x000D_
setTimeout(function() {_x000D_
console.log('Hi I am order 2');_x000D_
}, 2000);_x000D_
// callback();_x000D_
//}_x000D_
_x000D_
_x000D_
_x000D_
//function c(callback){_x000D_
setTimeout(function() {_x000D_
console.log('Hi I am order 3');_x000D_
}, 1000);_x000D_
// callback();_x000D_
_x000D_
//}_x000D_
_x000D_
_x000D_
/*function d(callback){_x000D_
a(function(){_x000D_
b(function(){_x000D_
_x000D_
c(callback);_x000D_
_x000D_
});_x000D_
_x000D_
});_x000D_
_x000D_
_x000D_
}_x000D_
d();*/_x000D_
_x000D_
_x000D_
async function funa(){_x000D_
_x000D_
var pr1=new Promise((res,rej)=>{_x000D_
_x000D_
setTimeout(()=>res("Hi4 I am order 1"),3000)_x000D_
_x000D_
})_x000D_
_x000D_
_x000D_
var pr2=new Promise((res,rej)=>{_x000D_
_x000D_
setTimeout(()=>res("Hi4 I am order 2"),2000)_x000D_
_x000D_
})_x000D_
_x000D_
var pr3=new Promise((res,rej)=>{_x000D_
_x000D_
setTimeout(()=>res("Hi4 I am order 3"),1000)_x000D_
_x000D_
})_x000D_
_x000D_
_x000D_
var res1 = await pr1;_x000D_
var res2 = await pr2;_x000D_
var res3 = await pr3;_x000D_
console.log(res1,res2,res3);_x000D_
console.log(res1);_x000D_
console.log(res2);_x000D_
console.log(res3);_x000D_
_x000D_
} _x000D_
funa();_x000D_
_x000D_
_x000D_
_x000D_
async function f1(){_x000D_
_x000D_
await new Promise(r=>setTimeout(r,3000))_x000D_
.then(()=>console.log('Hi3 I am order 1'))_x000D_
return 1; _x000D_
_x000D_
}_x000D_
_x000D_
async function f2(){_x000D_
_x000D_
await new Promise(r=>setTimeout(r,2000))_x000D_
.then(()=>console.log('Hi3 I am order 2'))_x000D_
return 2; _x000D_
_x000D_
}_x000D_
_x000D_
async function f3(){_x000D_
_x000D_
await new Promise(r=>setTimeout(r,1000))_x000D_
.then(()=>console.log('Hi3 I am order 3'))_x000D_
return 3; _x000D_
_x000D_
}_x000D_
_x000D_
async function finaloutput2(arr){_x000D_
_x000D_
return await Promise.all([f3(),f2(),f1()]);_x000D_
}_x000D_
_x000D_
//f1().then(f2().then(f3()));_x000D_
//f3().then(f2().then(f1()));_x000D_
_x000D_
//finaloutput2();_x000D_
_x000D_
//var pr1=new Promise(f3)_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
async function f(){_x000D_
console.log("makesure");_x000D_
var pr=new Promise((res,rej)=>{_x000D_
setTimeout(function() {_x000D_
console.log('Hi2 I am order 1');_x000D_
}, 3000);_x000D_
});_x000D_
_x000D_
_x000D_
var result=await pr;_x000D_
console.log(result);_x000D_
}_x000D_
_x000D_
// f(); _x000D_
_x000D_
async function g(){_x000D_
console.log("makesure");_x000D_
var pr=new Promise((res,rej)=>{_x000D_
setTimeout(function() {_x000D_
console.log('Hi2 I am order 2');_x000D_
}, 2000);_x000D_
});_x000D_
_x000D_
_x000D_
var result=await pr;_x000D_
console.log(result);_x000D_
}_x000D_
_x000D_
// g(); _x000D_
_x000D_
async function h(){_x000D_
console.log("makesure");_x000D_
var pr=new Promise((res,rej)=>{_x000D_
setTimeout(function() {_x000D_
console.log('Hi2 I am order 3');_x000D_
}, 1000);_x000D_
});_x000D_
_x000D_
_x000D_
var result=await pr;_x000D_
console.log(result);_x000D_
}_x000D_
_x000D_
async function finaloutput(arr){_x000D_
_x000D_
return await Promise.all([f(),g(),h()]);_x000D_
}_x000D_
_x000D_
//finaloutput();_x000D_
_x000D_
//h(); _x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
_x000D_
Source: Stackoverflow.com