protractor custom expected condition fails with error - javascript

I'm trying to wait the browser with browser.wait with a custom ExpectedCondition like this
The FunctionReturningANumber returns only a number and the numberToCheck is the number to check the number for.
var conditionFn = function () {
return functionReturningANumber(param) === numberToCheck;
};
var condition = EC.and(conditionFn);
browser.wait(condition, 50000);
But if I execute this, I get the error: fn(...).then is not a function which basically says, that it expects an promise. I have looked up the documentation about ExpectedConditions, and the example for a custom one is like this:
// You can define your own expected condition, which is a function that
// takes no parameter and evaluates to a promise of a boolean.
var urlChanged = function() {
return browser.getCurrentUrl().then(function(url) {
return url === 'http://www.angularjs.org';
});
};
And I do not see how here a promise is created. I only see, that a boolean is returned, and the documentation says evaluates to a promise of a boolean which confuses me even more.
This above is for waiting a response from an API, this is caused, because the test triggers a backend process, which protractor then needs to wait for. If there is any better way of doing this, I would greatly appreciate a better way.
I am using protractor 3.1.1.
Any help really apprectiated.
Edit:
I found a way to solve this, for some reason the logical solution by #alecxe didn't work, even if it makes sense:
var numberFound = 0;
var done = false;
var check = function () {
numberFound = functionReturnungANumber(param);
if (numberFound != numberToCheck) {
setTimeout(check, 4000);
} else {
done = true;
}
};
check();
return done;
If I add this to the function and retrieve the return value in the test, which calls this function, and add a browser.wait(function () {
return done;
}); there it works.
It's not beautiful, but for some reason, its the only thing working.... for me at least.

It's just that you don't need to wrap your Expected Condition function into EC.and:
browser.wait(conditionFn, 5000);

Try this one.
browser.wait(conditionFn () {
return url === 'http://www.angularjs.org';
}, 8000);

Related

Function doesn't return anything (async?)

I'm trying to add XPATH evaluation into my form. When user fills XPATH, it's evaluated on a server using AJAX and then return true or false.
The problem is that this function seems to return undefined allways. I suppose it's because of asynchronious behaviour of JS so I used $.when but it didn't helped.
function evalXpath(xpath) {
var test = $.post('/api/test-xpath/', {'xpath': xpath});
test.done(function (data) {
console.log('BEFORE RETURN '+Boolean(data['success']));
return Boolean(data['success']);
})
}
$(document).ready(function () {
$('#id_xpath').on('change', function () {
var xpath = $("#id_xpath").val();
$.when(evalXpath(xpath)).done(function (evaluated) {
console.log('RETURNED '+evaluated);
$('#xpath-valid').text(evaluated ? 'VALID' : 'INVALID');
});
});
});
The console output (as you can see, it's still asynchronious):
Do you have any ideas?
You're really close. A couple of things:
You forgot to return the promise out of evalXpath.
To get proper promise value chaining (e.g., in order for your value from the callback inside evalXpath to then become the resolution value of the promise it returns), use then, not done.
Then when using evalXpath, there's no need for $.when.
So:
function evalXpath(xpath) {
var test = $.post('/api/test-xpath/', {'xpath': xpath});
return test.then(function (data) {
// ^^^^^^ ^^^^
console.log('BEFORE RETURN '+Boolean(data['success']));
return Boolean(data['success']);
})
}
// ...
evalXpath(xpath).then(function (evaluated) {
// ^^^^ (this one could be `done`, but let's be consistent)

Nodejs function returns before internal function completes

I am trying to return whether a user already exists in a MongoDB. Running console.log within collection.find() prints the correct amount (greater than 0). However, when userExists is called, it always returns false (0).
How do I make Javascript wait for these functions to complete before returning a value? I've read about jQuery's $.Deffered(), but this feels dirty to me, and it didn't work.
function userExists(db, uid){
var collection = db.get('users');
var ret = 0;
collection.find({"uid":uid},{},function(e,docs){
ret = docs.length
});
return ret > 0?true:false;
}
As some have noted, collection.find is asynchronous, so when you reach the next line in userExists (the line where you've got return ret > 0?true:false;), it's too early and the value of ret hasn't been set. Anywhere outside of the callback to collection.find (and any functions it calls in turn), the query hasn't happened yet.
There is (basically) no way to "pause" userExists until after the query, so you need to change your whole approach. What you need is the Continuation Pattern. This means that whatever you're doing with the result of collection.find has to happen in the callback.
I don't know what you're trying to do with ret, so this might mean big changes to how your code is organized. Here's an outline that I hope will give you the general idea:
function processResultAndDisplayToTheUser(ret) {
//everything that needs to happen after the value of ret is set
if (ret > 0) {
doSomething();
} else {
doSomethingElse();
}
}
function userExists(db, uid){
var collection = db.get('users');
//ret doesn't exist here
collection.find({"uid":uid}, {}, function(e,docs){
var ret = docs.length;
//ret finally exists, so pass it to a function and use it
processResultAndDisplayToTheUser(ret);
});
//ret doesn't exist here either
}
//ret still doesn't exist here
I took the hint and ended up restructuring my code. I created a function addUpdateUser(), did the count there, then ran the addUser() or updateUser() functions accordingly.
addUpdateUser(db, {
"uid" : uid,
});
function addUpdateUser(db, userInfo){
var collection = db.get('users');
collection.find({"uid":userInfo.uid},{},function(e,docs){
if(docs.length > 0){
updateUser(db, userInfo)
}else{
addUser(db, userInfo)
}
});
}
since collection.find is asynchronous method that doesn't return immediately you need to change your code to,
you can pass a callback function
function userExists(db, uid,callback){
var collection = db.get('users');
collection.find({"uid":uid},{},function(e,docs){
callback(docs.length);
});
}
now you can call this userExists function as
userExists(db, uid,function(ret){
//do something here
})

Returning from a parent function from inside a child function - Javascript

I'm relatively new to coding in JavaScript, and I've came across a problem. I like to nest functions to keep things orderly, but how would I exit from a parent function from inside a child function?
example:
function foo1() {
function foo2() {
//return foo1() and foo2()?
}
foo2();
}
See update under the fold
You can't. You can only return from the child function, and then return from the parent function.
I should note that in your example, nothing ever calls foo2 (As of your edit, something does). Let's look at a more real example (and one that comes up a lot): Let's say we want know if an array contains an entry matching some criterion. A first stab might be:
function doesArrayContainEntry(someArray) {
someArray.forEach(function(entry) {
if (entryMatchesCondition(entry)) {
return true; // Yes it does <-- This is wrong
}
});
return false; // No it doesn't
}
You can't directly do that. Instead, you have to return from your anonymous iterator function in a way to stop the forEach loop. Since forEach doesn't offer a way to do that, you use some, which does:
function doesArrayContainEntry(someArray) {
return someArray.some(function(entry) {
if (entryMatchesCondition(entry)) {
return true; // Yes it does
}
});
}
some returns true (and stops looping) if any call to the iterator function returns true; it returns false if no call to the iterator returned true.
Again, that's just one common example.
You've referred to setInterval below, which tells me that you're almost certainly doing this in a browser environment.
If so, your play function almost certainly has already returned by the time you want to do what you're talking about, assuming the game has any interaction with the user other than alert and confirm. This is because of the asynchronous nature of the environment.
For example:
function play() {
var health = 100;
function handleEvent() {
// Handle the event, impacting health
if (health < 0 {
// Here's where you probably wanted to call die()
}
}
hookUpSomeEvent(handleEvent);
}
The thing is, that play will run and return almost immediately. Then the browser waits for the event you hooked up to occur, and if it does, it triggers the code in handleEvent. But play has long-since returned.
Make a note whether the parent function should also return.
function foo1() {
bool shouldReturn = false;
function foo2() {
shouldReturn = true; // put some logic here to tell if foo1() should also return
return;
}
if (shouldReturn) {
return;
} else {
// continue
}
}
It only says that you can't return the parent function in the child function, but we can do a callback and make it happen.
function foo1(cb = () => null) {
function foo2() {
cb();
}
foo2();
}
foo1(() => {
// do something
});
We can use Promises for this:
const fun1 = async () => {
const shouldReturn = await new Promise((resolve, reject) => {
// in-game logic...
resolve(true)
})
if(shouldReturn) return;
}
if you wanna return from the parent function, then just resolve with true
Based on your comment, something like this might work as a main game loop.
function play() {
var stillPlaying = true;
while(stillPlaying) {
... play game ...
stillPlaying = false; // set this when some condition has determined you are done
}
}

Execute statement after return statement in Javascript

window.onbeforeunload = function() {
if (document.getElementById("parentpan").style.display == "block") {
return "You are logged out.";
Logout();
}
};
I want the logout() function to be called after the return statement, is it possible?
You can't execute anything after a return statement.
edit: the finally statement allows code execution after a return for cleanup purposes.
(This is a good example for an XY-Question: You are asking about Y while never telling us for what X you actually need it).
The best possible way and most efficient way is try, catch and finally
catch is optional in this
`try{
// do something
return;
} finally {
// call function after return
}`
https://youtu.be/Is_o_L-ZIS8 this is helpful for you
The return statement ends a function, you cannot execute code after it. You could do this:
ret = "You are logged out.";
Logout();
return ret;
What you need is to execute Logout asynchronously. This can be easily achieve in JavaScript by using the setTimeout function as others have said. Here's a method I commonly use to call functions asynchronously:
Function.prototype.async = function () {
setTimeout.bind(null, this, 0).apply(null, arguments);
};
This method pushes a function call onto the event loop immediately (after 0 ms). Hence the function is executed after the current code completes (which for you is after you return). Here's a simple example of how to use it:
alert.async("This will be displayed later.");
alert("This will be displayed first.");
Since the first alert is called asynchronously it will execute after the second call to alert. As simple as preceding your function call with async. This is what you would do in your case:
window.onbeforeunload = function () {
if (document.getElementById("parentpan").style.display === "block") {
Logout.async();
return "You are logged out.";
}
};
What's the disadvantage? Since the function is blocked on the event loop it may never get the chance to execute (hence the user will never logout). Such a situation may arise. It usually occurs when the control goes into an infinite loop, or hangs because of a blocking AJAX request.
Good news for you however, this happens on a very rare occasion. So don't worry about it. Just use setTimeout like everyone else is bantering you to and you'll do just fine. Personally I think you should log out before returning a message that "You are logged out.", but it's your application.
Happy New Year. Cheers!
In general if you want something to be executed after the function has returned, you can set a timer:
function myFunction() {
if (document.getElementById("parentpan").style.display == "block") {
setTimeout(Logout, 50); // Logout will be called 50ms later
return "You are logged out.";
}
};
However, as noted in comments, this is not a good idea for onbeforeunload, as the timer event will not be fired if the page finished unloading first.
Most of the other answerers are missing what you are trying to do here. You want window.onbeforeunload to act like window.confirm(). There is no way to act on the ok action in the onbeforeunload event.
What you would have to do is hook it up on onunload to do the action.
window.onbeforeunload = function () {
return "Your session will be logged out"
};
window.onunload = function () {
logout();
}
Problem with this is modern day browsers will kill a lot of processes that run in unload/beforeunload to "speed up" the browser so it is faster. So if it is asynchronous, you will have a race condition.
return means you are returning from the execution of the called function.When return statement is executed, system understands that the function execution is over and it will switch to the main program from which the function is called.
In the program, you can see a statement after return.But the system wont check that even.
If you have jquery in your project you can use defered mechanism. You can return promise object for ongoing tasks like this :
function task() {
var defered = $.Deferred();
setTimeout(defered.resolve , 5000);
return defered.promise();
}
function task2() {
var defered = $.Deferred();
setTimeout(defered.resolve , 10000);
return defered.promise();
}
function run() {
return $.when(task(),task2());
}
var promise = run();
promise.done(function(){
alert("All tasks has been completed");
});
Demo
You can use setTimeout to achieve this. Your code should be as below
window.onbeforeunload = function() {
if (document.getElementById("parentpan").style.display == "block") {
setTimeout(function(){
Logout();
}, 0);
return "You are logged out.";
}
};
This will make sure that Logout is executed after return statement.
var a = 10;
function b(){
a = 25;
return;
function a(){}
}
b();
document.write(a);
try it
I found two ways to approach this.
The first one is as stated above by Bhavsar Japan
1. Example with try, catch and finally
const example1 = () => {
try {
return console.log('will execute first')
} finally{
console.log('will execute second')
}
return 'will never execute'
}
const main = () => {
const message = example1();
console.log(message)
}
main()
2. Example with Promise.resolve
const example2 = () => {
Promise.resolve()
.then(() => console.log('will execute after return'));
return 'will execute first'
}
const main = () => {
const message = example2();
console.log(message);
}
main();
I just written a way to return a result and then call a callback, like this:
function after_return(result, callback) {
function returner(resolve) {
if (!resolve) {
new Promise((res) => returner(res)).then(callback);
return result;
} else {
resolve();
}
}
return returner(undefined);
}
function main(a, b) {
return after_return(a + b, (_) => {
console.log("DONE");
});
}
console.log(main(5, 4));
I'm guessing that Logout is a time-intensive process and you want to provide feedback to the user before executing it:
setTimeout(Logout,1);
return "You are logged out.";

How to execute a Javascript function only after multiple other functions have completed?

My specific problem is that I need to execute a (potentially) large number of Javascript functions to prepare something like a batch file (each function call adds some information to the same batch file) and then, after all those calls are completed, execute a final function to send the batch file (say, send it as an HTML response). I'm looking for a general Javascript programming pattern for this.
Generalize problem:
Given the Javascript functions funcA(), funcB(), and funcC(), I would to figure out the best way to order execution so that funcC is only executed after after funcA and funcB have executed. I know that I could use nested callback functions like this:
funcA = function() {
//Does funcA stuff
funcB();
}
funcB = function() {
//Does funcB stuff
funcC();
}
funcA();
I could even make this pattern a little more general by passing in callback parameters, however, this solution becomes quite verbose.
I am also familiar with Javascript function chaining where a solution might look like:
myObj = {}
myObj.answer = ""
myObj.funcA = function() {
//Do some work on this.answer
return this;
}
myObj.funcB = function() {
//Do some more work on this.answer
return this;
}
myObj.funcC = function() {
//Use the value of this.answer now that funcA and funcB have made their modifications
return this;
}
myObj.funcA().funcB().funcC();
While this solution seems a little cleaner to me, as you add more steps to the computation, the chain of function executions grows longer and longer.
For my specific problem, the order in which funcA, funcB, etc. are executed DOES NOT matter. So in my solutions above, I am technically doing more work than is required because I am placing all the functions in a serial ordering. All that matters to me is that funcC (some function for sending the result or firing off a request) is only called after funcA and funcB have ALL completed execution. Ideally, funcC could somehow listen for all the intermediate function calls to complete and THEN would execute? I hoping to learn a general Javascript pattern to solve such a problem.
Thanks for your help.
Another Idea:
Maybe pass a shared object to funcA and funcB and when they complete execution mark the shared object like sharedThing.funcA = "complete" or sharedThing.funcB = "complete" and then somehow? have funcC execute when the shared object reaches a state where all fields are marked complete. I'm not sure how exactly you could make funcC wait for this.
Edit:
I should note that I'm using server-side Javascript (Node.js) and I would like to learn a pattern to solve it just using plain old Javascript (without the use of jQuery or other libraries). Surely this problem is general enough that there is a clean pure-Javascript solution?
If you want to keep it simple, you can use a counter-based callbacks system. Here's a draft of a system that allows when(A, B).then(C) syntax. (when/then is actually just sugar, but then again the whole system arguably is.)
var when = function() {
var args = arguments; // the functions to execute first
return {
then: function(done) {
var counter = 0;
for(var i = 0; i < args.length; i++) {
// call each function with a function to call on done
args[i](function() {
counter++;
if(counter === args.length) { // all functions have notified they're done
done();
}
});
}
}
};
};
Usage:
when(
function(done) {
// do things
done();
},
function(done) {
// do things
setTimeout(done, 1000);
},
...
).then(function() {
// all are done
});
If you don't use any asynchronous functions and your script doesn't break the order of execution, then the most simple solution is, as stated by Pointy and others:
funcA();
funcB();
funcC();
However, since you're using node.js, I believe you're going to use asynchronous functions and want to execute funcC after a async IO request has finished, so you have to use some kind of counting mechanisms, for example:
var call_after_completion = function(callback){
this._callback = callback;
this._args = [].slice.call(arguments,1);
this._queue = {};
this._count = 0;
this._run = false;
}
call_after_completion.prototype.add_condition = function(str){
if(this._queue[str] !== undefined)
throw new TypeError("Identifier '"+str+"' used twice");
else if(typeof str !== "String" && str.toString === undefined)
throw new TypeError("Identifier has to be a string or needs a toString method");
this._queue[str] = 1;
this._count++;
return str;
}
call_after_completion.prototype.remove_condition = function(str){
if(this._queue[str] === undefined){
console.log("Removal of condition '"+str+"' has no effect");
return;
}
else if(typeof str !== "String" && str.toString === undefined)
throw new TypeError("Identifier has to be a string or needs a toString method");
delete this._queue[str];
if(--this._count === 0 && this._run === false){
this._run = true;
this._callback.apply(null,this._args);
}
}
You can simplify this object by ignoring the identifier str and just increasing/decreasing this._count, however this system could be useful for debugging.
In order to use call_after_completion you simply create a new call_after_completion with your desired function func as argument and add_conditions. func will only be called if all conditions have been removed.
Example:
var foo = function(){console.log("foo");}
var bar = new call_after_completion(foo);
var i;
bar.add_condition("foo:3-Second-Timer");
bar.add_condition("foo:additional function");
bar.add_condition("foo:for-loop-finished");
function additional_stuff(cond){
console.log("additional things");
cond.remove_condition("foo:additional function");
}
for(i = 0; i < 1000; ++i){
}
console.log("for loop finished");
bar.remove_condition("foo:for-loop-finished");
additional_stuff(bar);
setTimeout(function(){
console.log("3 second timeout");
bar.remove_condition("foo:3-Second-Timer");
},3000);
JSFiddle Demo
If you don't want to use any helper libraries, than you need to write some helper yourself, there's no simple one line solution for this.
If you'd like to end with something that looks as readable as it would in synchronous case, try some deferred/promise concept implementation (it's still plain JavaScript), e.g. using deferred package you may end up with something as simple as:
// Invoke one after another:
funcA()(funcB)(funcC);
// Invoke funcA and funcB simultaneously and afterwards funcC:
funcA()(funcB())(funcC);
// If want result of both funcA and funcB to be passed to funcC:
deferred(funcA(), funcB())(funcC);
Have a look into jQuery's deferred objects. This provides a sophisticated means of controlling what happens when in an asynchronous environment.
The obvious use-case for this is AJAX, but it is not restricted to this.
Resources:
jQuery docs: deferred object
good introduction to deferred object patterns
Non-AJAX use for jQuery's deferred objects
I was looking for the same kind of pattern. I am using APIs that interrogate multiple remote data sources. The APIs each require that I pass a callback function to them. This means that I cannot just fire off a set of my own functions and wait for them to return. Instead I need a solution that works with a set of callbacks that might be called in any order depending on how responsive the different data sources are.
I came up with the following solution. JS is way down the list of languages that I am most familiar with, so this may not be a very JS idiom.
function getCallbackCreator( number_of_data_callbacks, final_callback ) {
var all_data = {}
return function ( data_key ) {
return function( data_value ) {
all_data[data_key] = data_value;
if ( Object.keys(all_data).length == number_of_data_callbacks ) {
final_callback( all_data );
}
}
}
}
var getCallback = getCallbackCreator( 2, inflatePage );
myGoogleDataFetcher( getCallback( 'google' ) );
myCartoDataFetcher( getCallback( 'cartodb' ) );
Edit: The question was tagged with node.js but the OP said, "I'm looking for a general Javascript programming pattern for this," so I have posted this even though I am not using node.
Nowadays, one can do something like this:
Let's say we have both funcA, funcB and funcC:
If one's want funcA and funcB results to be passed to funcC:
var promiseA = new Promise((resolve, reject) => {
resolve(await funcA());
});
var promiseB = new Promise((resolve, reject) => {
resolve(await funcB());
});
var promise = Promise.all([ promiseA, promiseB ]).then(results => {
// results = [result from funcA, result from funcB]
return funcC(results);
});
If one's want funcA, then funcB and then funcC:
var promise = (
new Promise(async resolve => resolve( await funcA() ))
).then(result_a => funcB(result_a)).then(result_b => funcC(result_b));
And finally:
promise.then(result_c => console.log('done.'));
how about:
funcC(funcB(funcA)));
I think the questions is because some of functions run longer and there might be a situation when we run funcC when funcA or funcB did not fininsh executing.

Categories