Memory leak in while with promises - javascript

I have a nodejs cluster with a primary that handles worker cycles (in the while loop) and that listens to worker messages to progress in the cycle.
(In my code index.js does not send messages on setInterval but on other type of event, I have here simplified the code to get the essence of the problem)
Server.js
var cluster = require('cluster');
const ClusterMessages = require('cluster-messages');
const messages = new ClusterMessages();
if (cluster.isMaster){
let worker = cluster.fork()
console.log(cluster);
(async ()=>{
let cycle = 0
while(true){
console.log(cycle);
cycle ++
await Promise.all([
enough(),
])
}
function enough () {
return new Promise(resolve => {
messages.on('enough', () => {
console.log('enough');
resolve()
});
});
}})()
} else {
require('./index.js')
}
Index.js
const ClusterMessages = require('cluster-messages');
const messages = new ClusterMessages();
setInterval(() => {
messages.send('enough');
}, 1000);
The code is working fine (as such, in this example and in my code) but there seems to be a memory leak as you can understand from the output of this code:
0
enough
1
enough
enough
2
enough
enough
enough
3
enough
enough
enough
enough...
I tried several things like exchanging new Promise and messages.on(), add a return in the callback of the promise but I have no clue what is happening here. Any ideas?
The solution is to make another event that can be triggered once contrary to the 'event listener' of the cluster-messages package
Server.js
if (cluster.isMaster){
let worker = cluster.fork()
console.log(cluster);
// Importing events
const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
messages.on('enough', () => {
eventEmitter.emit('event');
});
(async ()=>{let cycle = 0
while(true){
console.log(cycle);
cycle ++
await Promise.all([
enough(),
])
}
function enough () {
return new Promise(resolve => {
eventEmitter.once('event', () => {
console.log('event');
resolve()
});
});
}})()
} else {
require('./index.js')
}
Index.js
const ClusterMessages = require('cluster-messages');
const messages = new ClusterMessages();
setInterval(() => {
messages.send('enough');
}, 1000);

Every call of enough() installs another listener for the enough event on messages. They never get removed, leaking memory (and leading to an increasing number of logs per event). Instead, use the once method to install the listener:
function enough () {
return new Promise(resolve => {
messages.once('enough', () => {
// ^^^^
console.log('enough');
resolve();
});
});
}
Or even simpler, using once:
const { once } = require('events');
function enough() {
return once(messages, 'enough');
}
In your particular example, I would recommend not to use promises to handle the events. You might even miss events that are fired while you are removing and re-attaching a listener. Just write
let cycle = 0
messages.on('enough', () => {
console.log(cycle);
cycle++;
});
If for some reason you need a loop that you can break from or await other things in, I would recommend an asynchronous iterator, built with on:
const { once } = require('events');
(async () => {
let cycle = 0
for await (const _ of on(messages, 'enough')) {
console.log(cycle);
cycle++;
}
})();

Related

How to loop through jest tests synchronously

I am currently looping through a Jest test suite multiple times in my code and need to access a value from the afterAll block after each test iteration.
Once all iterations and tests are finished, I need to do some calculations for these values from the afterAll block.
The problem is the tests being run in the loop are asynchronous and by the time I get to my calculate function, there's no values yet.
So My current effort is to return a Promise for each iteration and resolving the value I need, however this loop stops just after 1 iteration. It will complete 1 test successfully, and the 2nd test will reach the describe block, but will never reach the beforeAll block or anything after.
How can I wait for all tests in the loop to finish, before moving on to my calculations function?
Here is part of my code:
function startTestSuite(testDetails, config) {
return new Promise((resolve, reject) => {
describe('Test Google page', () => {
jest.setTimeout(JEST_TIMEOUT);
beforeAll(async () => {
timerA = performance.now();
browser = await new BrowserDriver(testDetails);
await browser.build(config);
});
afterAll(async () => {
await browser.quit();
timerB = performance.now();
const totalTime = Math.ceil(timerB - timerA);
resolve(totalTime);
});
test('should navigate to Google and get title', async () => {
await browser.navigateTo(TEST_URL, testDetails);
const title = await browser.getTitle(testDetails);
expect(title).toEqual('Google');
});
});
});
}
async function startTests() {
const testDetails = {
testCount: 0,
tunnelId,
tunnelName,
buildId
};
for (let i = 1; i < NUMBER_OF_TESTS; i++) {
testDetails.testCount = i;
const config = configSauceLabs(testDetails);
const totalTime = await startTestSuite(testDetails, config);
performanceArr.push(totalTime);
}
// Run calculations function
}
startTests();

Using RxJS how to buffer function calls until an other async function call has resolved

How can I use RxJS to buffer function calls until another async function has resolved?
Here is a simple example of what I'd like to accomplish
function asyncFunc(time) {
setTimeout(() => {
console.log('asyncFunc has resolved');
}, time);
}
function funcToBuffer(time) {
setTimeout(() => {
console.log(time);
}, time);
}
asyncFunc(3000);
funcToBuffer(1000);
funcToBuffer(2000);
funcToBuffer(4000);
funcToBuffer(5000);
asyncFunc(8000);
funcToBuffer(6000);
funcToBuffer(7000);
At the moment this code will print:
1000
2000
asyncFunc has resolved
4000
5000
6000
7000
asyncFunc has resolved
What I want is for this to print:
asyncFunc has resolved
1000
2000
4000
5000
asyncFunc has resolved
6000
7000
In essence, I want some kind of control flow that allows me to call funcToBuffer whenever I feel like, but under the hood, I want it to hold on executing whenever asyncFunc is executing and waiting to be resolved. Once asyncFunc has resolved, funcToBuffer calls should no longer be buffered and be executed right away.
I have tried playing with the buffer operator but I wasn't able to achieve the desired outcome.
If I understand it right, your main goal is to control the execution of a sequence of functions through a mechanism that buffers them until something happens, and that something is exactly what triggers the execution of the functions buffered.
If this is correct, the following could be the basis for a possible solution to your problem
const functions$ = new Subject<() => any>();
const buffer$ = new Subject<any>();
const executeBuffer$ = new Subject<any>();
const setBuffer = (executionDelay: number) => {
buffer$.next();
setTimeout(() => {
executeBuffer$.next();
}, executionDelay);
}
const functionBuffer$ = functions$
.pipe(
bufferWhen(() => buffer$),
);
zip(functionBuffer$, executeBuffer$)
.pipe(
tap(functionsAndExecuteSignal => functionsAndExecuteSignal[0].forEach(f => f()))
)
.subscribe();
Let me explain a bit the code.
First thing, we build functions$, i.e. an Observable of the functions we want to control. The Observable is built using a Subject, since we want to be able to control the notification of such Observable programmatically. In other words, rather than kicking the execution of a function like this funcToBuffer(1000), we create the function (as an object) and ask the functions$ Observable to emit the function like this
const aFunction = () => setTimeout(() => {console.log('I am a function that completes in 1 second');}, 1000);
functions$.next(aFunction);
In this way we have created a stream of functions that eventually will be executed.
Second thing, we create 2 more Observables, buffer$ and executeBuffer$, again using Subjects. Such Observables are used to signal when we have to create a buffer out of the functions emitted so far by functions$ and when we have to start the execution of the functions buffered.
These last 2 Observables are used in the function setBuffer. When you call setBuffer you basically say: please, create a buffer with all the functions which have been emitted so far by functions$ and start executing them after the executionDelay time specified as parameter.
The buffering part is performed by the functionBuffer$ Observable which is created using bufferWhen operator. The execution part is implemented leveraging the zip operator, that allows us to set the rhythm of execution of the functions based on the emissions of executeBuffer$ Observable.
You can test the above code setting up the following test data.
let f: () => any;
setBuffer(3000);
f = () => setTimeout(() => {console.log('f1');}, 1000);
functions$.next(f);
f = () => setTimeout(() => {console.log('f2');}, 2000);
functions$.next(f);
f = () => setTimeout(() => {console.log('f4');}, 4000);
functions$.next(f);
f = () => setTimeout(() => {console.log('f5');}, 5000);
functions$.next(f);
setBuffer(8000);
f = () => setTimeout(() => {console.log('f6');}, 6000);
functions$.next(f);
f = () => setTimeout(() => {console.log('f7');}, 7000);
functions$.next(f);
setBuffer(16000);
I started working on a solution with combineLatest but figured that a BehaviorSubject would be a better solution once I put more thought into it.
const { BehaviorSubject } = rxjs;
const { filter } = rxjs.operators;
let finalised$ = new BehaviorSubject(false);
function asyncFunc(time) {
setTimeout(() => {
console.log('asyncFunc has resolved');
if (!finalised$.getValue()) {
finalised$.next(true);
}
}, time);
}
function funcToBuffer(time) {
finalised$.pipe(filter(finalised => finalised)).subscribe(_ => { // Filter so only fire finalised being true
setTimeout(() => {
console.log(time);
}, time);
});
}
asyncFunc(3000);
funcToBuffer(1000);
funcToBuffer(2000);
funcToBuffer(4000);
funcToBuffer(5000);
asyncFunc(8000);
funcToBuffer(6000);
funcToBuffer(7000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.2.2/rxjs.umd.min.js"></script>
CombineLatest that waits for both obsevables to fire.
const { of, combineLatest } = rxjs;
const { delay } = rxjs.operators;
let obs1$ = of(1).pipe(delay(1000));
let obs2$ = of(2).pipe(delay(2000));
let now = new Date();
combineLatest(obs1$, obs2$).subscribe(([obs1, obs2]) => {
let ellapsed = new Date().getTime() - now.getTime();
console.log(`${obs1} - ${obs2} took ${ellapsed}`);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.2.2/rxjs.umd.min.js"></script>

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

Toggling API call by a stream

Here:
import Rx from 'rxjs';
function fakeApi(name, delay, response) {
return new Rx.Observable(observer => {
console.log(`${name}: Request.`)
let running = true;
const id = setTimeout(() => {
console.log(`${name}: Response.`)
running = false;
observer.next(response);
observer.complete();
}, delay);
return () => {
if(running) console.log(`${name}: Cancel.`)
clearTimeout(id);
}
})
}
function apiSearch() { return fakeApi('Search', 4000, "This is a result of the search."); }
//============================================================
const messages$ = new Rx.Subject();
const toggle$ = messages$.filter(m => m === 'toggle');
const searchDone$ = toggle$.flatMap(() =>
apiSearch().takeUntil(toggle$)
);
searchDone$.subscribe(m => console.log('Subscriber:', m))
setTimeout(() => {
// This one starts the API call.
toggle$.next('toggle');
}, 2000)
setTimeout(() => {
// This one should only cancel the API call in progress, not to start a new one.
toggle$.next('toggle');
}, 3000)
setTimeout(() => {
// And this should start a new request again...
toggle$.next('toggle');
}, 9000)
my intent is to start the API call and stop it when it is in progress by the same toggle$ signal. Problem with the code is that toggle$ starts a new API call every time. I would like it not to start the new call when there is one already running, just to stop the one which is already in progress. Some way should I "unsubscribe" the outermost flatMap from toggle$ stream while apiSearch() is running. I guess that there is a need to restructure the code to achieve the behaviour... What is the RxJS way of doing that?
UPDATE: After some more investigations and user guide lookups, I came with this:
const searchDone$ = toggle$.take(1).flatMap(() =>
apiSearch().takeUntil(toggle$)
).repeat()
Works like it should. Still feels cryptic a little bit. Is this how you RxJS guys would solve it?
I think your solution will work only once since you're using take(1). You could do it like this:
const searchDone$ = toggle$
.let(observable => {
let pending;
return observable
.switchMap(() => {
let innerObs;
if (pending) {
innerObs = Observable.empty();
} else {
pending = innerObs = apiSearch();
}
return innerObs.finally(() => pending = null);
});
});
I'm using let() only to wrap pending without declaring it in parent scope. The switchMap() operator unsubscribes for you automatically without using take*().
The output with your test setTimeouts will be as follows:
Search: Request.
Search: Cancel.
Search: Request.
Search: Response.
Subscriber: This is a result of the search.

How to wait until a predicate condition becomes true in 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;
}

Categories