Javascript: Pause while a condition is true - javascript

I am interfacing with a third party SDK in my javascript code. I make func1 and func2. Func1 is wired to a "onFunc1" event where I keep track of a flag. When the flag is false, I try to call Func2. My objective is to issue Func1, wait until it finishes(the only way I know it finishes is when onFunc1 event is fired) and then, issue Func2. This is how my code is:
var _connected = false;
var _customWindow = CustomWindow.initialize();
_customWindow.on('Func1', function(){ window._connected = false; });
const delay = ms => new Promise(res => setTimeout(res, ms));
function goToNextWindow(){
try{
_customWindow.Func1();
} catch(err){console.log('error calling Func1');}
console.log('wait for Func1 to finish');
while(window._connected === true){
(async () => {
await delay(300);
})();
}
console.log('Func1 execution is done');
if(window._connected === false){_customWindow.Func2();}
}
function button_click(){
goToNextWindow();
}
I'm trying to make sure that Func1 is done executing before I issue the Func2 call. Is this a correct way of accomplishing my task? Can this be simplified?
Any help is appreciated.

You can create a promise that will resolve when you set window._connected to false. Then you can wait for that promise to resolve in your function:
let _connected = false;
let _customWindow = CustomWindow.initialize();
const waitToBeFalse = new Promise((resolve) => {
_customWindow.on('Func1', function(){
window._connected = false;
resolve();
});
});
async function goToNextWindow(){
try{
_customWindow.Func1();
} catch(err){console.log('error calling Func1');}
console.log('wait for Func1 to finish');
await waitToBeFalse;
if(window._connected === false){_customWindow.Func2();}
}

Related

Why does this sequence of await function calls run in the wrong order?

I want to output some text after 2 seconds first, after output some "alert()" second and at the end output some "console.log" by using only async/await. Please help me how to write such a sequence?
Why the code below does not work
async function qaz()
{
let res1 = await setTimeout(function(){
console.log("show me first");
}, 2000);
let res2 = await alert('show me second');
let res3 = await console.log('show me at the end');
return [res1,res2,res3];
}
The setTimeout is part of the JavaScript asynchronous methods (methods that are starting to execute and their result will return sometime in the future to a component called the callback queue, later to be executed)
What you probably want to do is to wrap the setTimeout function within a Promise and await it and then execute the rest of the synchronous code.
const longTask = () => new Promise(resolve => {
setTimeout(function(){
console.log("show me first");
resolve();
}, 2000);
});
async function qaz()
{
await longTask();
alert('show me second');
console.log('show me at the end');
}
qaz();
I suggest to read more about the event loop model here
Neither the setTimeout, the alert, or the console.log return promises, and that's a problem because await only works with promises.
You can still use async/await, however. Create a delay function that returns a promise you can await for the first part, and then after that resolves do your alerts and your logging.
function delay(n) {
return new Promise(res => {
setTimeout(() => res(), n);
});
}
async function qaz() {
await delay(2000);
console.log('Show me first');
alert('Show me second');
console.log('Show me at the end');
}
qaz();

Javascript delay a loop till iframe is loaded in first iteration

I have a javascript loop that executes a couple of functions. The first function loads an iframe and the second function clicks an element on that iframe. I want the loop not to run the second function until the iframe is finished loading. But I am not sure how to achieve this.
So far I have done this but doesn't seem to do the job
Loop :
action.steps.forEach(step => {
window[step.functionName](step.functionParameter);
});
First function
function goToUrl(url) {
let iframeDocument = document.querySelector('iframe');
iframeDocument.src = url;
let iframeLoaded;
iframeDocument.onload = () => {
iframeLoaded = true
}
async function checkLoad() {
if (iframeLoaded) {
alert("page loaded");
return true;
} else {
await sleep(500);
checkLoad();
}
}
checkLoad();
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
second function
function clickOnElement(elementSelector) {
var element = iframeDocument.querySelector(elementSelector);
element.click();
}
The first function is an asynchronous operation, which can be modified to return a Promise that resolves when the frame is loaded. This also means you don't need to recursively checkLoad.
function goToUrl(url) {
const iframeDocument = document.querySelector('iframe');
iframeDocument.src = url;
return new Promise((resolve, reject) => {
// calls `resolve()` when loaded
iframeDocument.onload = resolve;
});
}
The second function needs to wait for the first function to be resolved.
To generalise this pattern, you can modify your loop as an asynchronous function,
which awaits for the result of a step if that step's function returns a Promise (e.g. your goToUrl function:
async function yourLoop() {
// each step could be synchronous or asynchronous
for (const step of actions.step) {
const result = window[step.functionName](step.functionParameter);
if (result instanceof Promise) {
// if step is asynchronous operation, wait for it to complete
await result;
}
}
}
/////// usage ////////
yourLoop().then(() => {
/* all steps completed */
}).catch(() => {
/* some step(s) failed */
});
Whenever a timer or event handler is needed, I usually prefer not to do a traditional loop nor mess with promises/awaits.
Instead what I would do is something similar to this. Basically waiting in onload to run the callback function then moving on to the next function in the loop.
let max = action.steps.length;
let cur = 0;
function loadIframe(url) {
let iframeDocument = document.querySelector('iframe');
iframeDocument.src = url;
let iframeLoaded;
iframeDocument.onload = () => {
let step = action.steps[cur];
window[step.functionName](step.functionParameter);
if(cur < max){
cur++;
loadIframe(step.url)
}
}
}
loadIframe(action.steps[cur].url);

Calling a function as many times as possible in a given time interval

I am trying to call the function test() as many times as possible in a given time interval.
Here the function should be running for 15 seconds.
function test(): void; // Only type def
function run() {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 15000); // 15 seconds
while (true) {
test();
}
});
}
run()
.then(() => {
console.log('Ended');
});
However, the function doesn't stop running, and the Ended console.log does not appear. (Promise not resolved obviously). Is there a way to achieve this in Javascript ?
I was wondering, I could probably use console timers and put the condition in the while statement ? (But is that the best way ?)
The reason why your function does not stop executing is because resolving a promise does not stop script executing. What you want is to store a flag somewhere in your run() method, so that you can flip the flag once the promise is intended to be resolved.
See proof-of-concept below: I've shortened the period to 1.5s and added a dummy test() method just for illustration purpose:
let i = 0;
function test() {
console.log(`test: ${i++}`);
}
function run() {
return new Promise(resolve => {
let shouldInvoke = true;
setTimeout(() => {
shouldInvoke = false;
resolve();
}, 1500); // 15 seconds
const timer = setInterval(() => {
if (shouldInvoke)
test();
else
window.clearInterval(timer);
}, 0);
});
}
run()
.then(() => {
console.log('Ended');
});

javascript callback - execution of scripts is in random order

I have this problem - while executing this function
function(){
var after = function(){
server.executescript("scriptname")
};
var before = function (callback) {
server.executescript("scriptname2")
callback();
};
before(after);
}
execution of scripts is in random order and not as I would expect:
scriptname2
scriptname
what am I missing?
Thank you!
this seems to work
function(){
function after(){
server.executescript("scriptname");
}
function before(){
return new Promise((resolve, reject)=>{
server.executescript("scriptname2");
resolve();
});
}
before().then(after);
}
thank you all for participating!
The order you are seeing is the one expected:
var after = async function() {
await console.log("scriptname");
};
var before = async function (callback) {
await console.log("scriptname2");
callback();
};
before(after);
You first call the "before()" function which is going to call your "scriptname2".
Then you will call your "callback()" which is your "after()" function.
Which is going to call your "scriptname".

Test that a function awaits something before doing anything else

Suppose I have a function that executes an asynchronous action (doStuffAsync()) and then intends to do some other stuff (doOtherStuff()).
doStuffAsync() returns a Promise.
Also assume everything is mockable.
How do I test that my function awaits doStuffAsync() before trying to doOtherStuff()?
I thought of mocking doStuffAsync() using resolve => setTimeout(resolve(), timeout), but timeout-based testing looks very fragile.
You need a flag accessible by both doStuffAsync and doOtherStuff.
In doStuffAsync() write in that flag
In doOtherStuff() read from that flag and determine if it was written
Something like:
var isAsyncRunning = false;
function doStuffAsync(){
isAsyncRunning = true;
new Promise(function(resolve, reject) {
setTimeout(()=>{
isAsyncRunning = false;
resolve(); //irrelevant in this exercise
}, 1000);
});
}
doStuffAsync();
function doOtherStuff(){
if(isAsyncRunning){
console.log("Async is running.");
} else {
console.log("Async is no longer running.");
};
}
doOtherStuff();
setTimeout(() => {
//calling doOtherStuff 2 seconds later..
doOtherStuff();
}, 2000);
I managed to complete it with a less ugly solution than setTimeout – setImmediate.
function testedFunction() {
await MyModule.doStuffAsync();
MyModule.doOtherStuff();
}
it('awaits the asynchronous stuff before doing anything else', () => {
// Mock doStuffAsync() so that the promise is resolved at the end
// of the event loop – which means, after the test.
// -
const doStuffAsyncMock = jest.fn();
const delayedPromise = new Promise<void>(resolve => setImmediate(resolve()));
doStuffAsyncMock.mockImplementation(() => delayedPromise);
const doOtherStuffMock = jest.fn();
MyModule.doStuffAsync = doStuffAsyncMock;
MyModule.doOtherStuffMock = doOtherStuffMock;
testedFunction();
expect(doOtherStuffMock).toHaveBeenCalledTimes(0);
}
setImmediate will put off the resolution of your promise to the end of the event loop, which is after your test completes.
So, your assert that doOtherStuff() was not invoked:
Will pass if there is an await inside the testedFunction
Will fail if there isn't.

Categories

Resources