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".
Related
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();}
}
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);
I have this function:
waitForFreeAccnt.prototype.isMemberFree = function () {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return true;
} else {
console.log('it is not free');
return false;
}
});
};
I would like to wait till the account is free for up to 10 seconds with something like that:
var test = function () {
for (var start = 1; start < 10; start++) {
var result = self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
But it doesn't work because self.api.getMemberInfo is asynch call. This is super frustrating with Javascript. Any other language it would be so simple to do. How do I force the for loop to wait for self.isMemberFree() to finish executing before proceeding with the loop?
Also to note, this is not in browser execution so I don't care about anything hanging.
When dealing with asynchronous code, you need to make use of callbacks. That is, if you want to do a() and b() in order but a() does something asynchronously, then you need to call b() from within a() once a() has a result. So not:
a(); // does something asynchronously
b(); // tries to use a()'s result but it isn't available yet
... but rather
a(b); // pass b to a() and a() will call it when ready
function a(callback) {
triggerAsyncFunction(function(result) {
if (result === something)
callback("a just finished");
});
}
Note that a() doesn't refer to b() by name, it just calls whatever function is passed in as an argument.
So applying that to your code, maybe something like this:
waitForFreeAccnt.prototype.isMemberFree = function (cbf) {
var self = this;
self.api.getMemberInfo(function () {
cbf(self.api.connect.accountType === 'FREE');
});
};
waitForFreeAccnt.prototype.testMemberXTimes = function(maxAttempts, callback) {
var attempts = 0;
var self = this;
(function attempt() {
self.isMemberFree(function(free) {
if (free)
callback(true);
else if (++attempts < maxAttempts)
setTimeout(attempt, 1000);
else
callback(false);
});
)();
};
this.testMemberXTimes(10, function(isFree) {
// the next part of your code here, or called from here
// because at this point we know we've tested up to
// ten times and isFree tells us the result
});
Note that the way I coded getMemberInfo() it is basically doing the same thing yours was, but instead of returning a boolean it is calling the callback function and passing the same boolean value that you were returning. (I've removed the console.log()s to make the code shorter.)
Note also that you could structure the above to use promises, but the end result will be the same.
You could return a Promise
waitForFreeAccnt.prototype.isMemberFree = function () {
return new Promise((reject, resolve)=>
// set a timeout if api call takes too long
var timeout = setTimeout(()=> reject(Error('API timeout')), 10000);
// make api call
this.api.getMemberInfo(()=> {
clearTimeout(timeout);
resolve(this.api.connect.accountType === 'FREE');
});
);
};
Then use it like this
whatever.isMemberFree().then(isFree=> {
if (isFree)
console.log('it is free');
else
console.log('it is not free');
})
// handle timeout or other errors
.catch(err=> {
console.log(err.message);
});
Building on naomik's answer, if you do it that way you can pretty easily use a for loop with it, using the (most likely) upcoming async/await feature - though it's not part of ES2015.
// Note "async" here! That will make "await" work. It makes the function
// return a promise, which you'll be able to either "await" or
// "test().then" later.
var test = async function () {
for (var start = 1; start < 10; start++) {
// Right here we're using "await" - it makes JavaScript *wait* for
// the promise that comes from self.isMemberFree() to be finished.
// It's really handy because you can use it in loops like "for" and
// "while" without changing the flow of your program!
var result = await self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
For now you'll need to use a transpiler like Babel or Traceur before you can really use async/await, though. It's only supported in Microsoft Edge 14 right now.
And a big emphasis that what is returned from test() isn't whatever you directly return from inside it. If I do this:
var test = async function() { return 15; };
var result = test();
I'm not going to get 15 - I'll get a promise that will resolve as 15:
result.then(function(res) {
console.log(res); // 15
});
// or, use an async function again:
var main = async function() {
console.log(await res); // 15
};
main();
I don't have my work laptop today because it is Sunday, I'm coding this on sublime. Apologise if the syntax is a bit off.
To solve your problem I would recommend changing isMemberFree() to take in a callback function. This is because isMemberFree is async, and you will need a way to report the result after it has done the work.
Then change test function to use setTimeout API to wait a second.
Wrap the function call for isMemberFree() to be in a nested function and call it recursively, that way you'll have synchronize control over the async calls.
Look at the coding example:
waitForFreeAccnt.prototype.isMemberFree = function (done) {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return done(null, true);
} else {
console.log('it is not free');
return done(null, false);
}
});
};
var test = function () {
var testMembership = function(waitAttempt, isFree) {
if (isFree) {
return;
}
else if (waitAttempt > 10) {
// wait exceeded, do something.
return;
}
setTimeout(function() {
self.isMemberFree(function(err, isFree) {
testMembership(waitAttempt+=1, isFree);
});
}, /*total milliseconds in 1sec=*/1000);
}
testMembership(/*WaitAttempts=*/0, /*isFree=*/false);
};
What the above code does is that, presumably something has already been done to the member's account and now test function is called. So it waits for 1 second, then call isMemberFree function, this happens recursively until either isMemberFree() returns true OR the 10 seconds wait has been exceeded.
I have 3 functions in my javascript code. I want to call the 3rd function as soon as both function1 and function2 are over executing.
func_one();
func_two();
func_three(); // To be called as soon both of the above functions finish executing.
Note that they may take variable time since function 1 and 2 are for fetching geolocation and some ajax request respectively.
How about this?
func_one() {
// Do something
func_two();
}
func_two() {
// Do something
func_three();
}
func_three() {
// Do something
}
There are two ways to solve this problem.The first one is to create callback functions as parameters for your existing functions.
function one(param_1 .. param_n, callback) {
var response = ... // some logic
if (typeof callback === "function") {
callback(response);
}
}
The second way is to use Promises like:
var p1 = new Promise(
function(resolve, reject) {
var response = ... //some logic
resolve(response);
}
}
var p2 = new Promise( ... );
p1.then(function(response1) {
p2.then(function(response2) {
//do some logic
})
})
You can try like this
var oneFinish = false;
var twoFinish = false;
function one(){
//...
oneFinish = true; // this value may depends on some logic
}
function two(){
//...
twoFinish = true; // this value may depends on some logic
}
function three(){
setInterval(function(){
if(oneFinish && twoFinish){
//...
}
}, 3000);
}
Since your functions func_one and func_two are making service calls you can call the functions on after other at the success callback of the previous function. Like
func_one().success(function(){
func_two().success(function(){
func_three();
});
});
Considering code snippet below,
function one(){
var prm = new Promise(function(resolve,reject){
});
prm.customKey = function(){
}
return prm;
}
function two(){
return one().then(function(){
//something
});
}
Now calling the function two, returns a promise in which 'customKey' is missing
function three(){
return one();
}
But while doing the same thing in function three(without handling success using 'then'),
returns a promise which has 'customKey' in it.
Can someone clarify me whats really happening and why?
As you can see from "Mozilla Docs" then method returns new Promise (e.g: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then).
If you want to assign a function to retrieve / do something in resolve function do it like this:
var prm = new Promise((resolve, reject) => {
let customKeyFunction = () => {
console.log("I'm custom key function.");
};
resolve(customKeyFunction);
})
From now on you can call this function like that:
function two() {
return one().then(customKey => {
customKey();
})
}