javascript WAIT for something to be true - javascript

i create in a javascript function a prototype window. in the window i load a site where the user has to select something. i want the javascript function to wait until the user selected something and then return the value of the what the user selected.
function showTargetDirectoryChooser(){
var win = new Window( 'dirchooser_' + new Date().getTime() , {className: 'alphacube', width: 320, height: 470, url: '/directories/choose', maximizable: false});
win.showCenter();
win.setDestroyOnClose();
// WAIT_UNTIL( win.content.contentWindow.Directory != null )
return win.content.contentWindow.Directory
}
i found here something i could maybe use - but i dont understand how to...

This is an asynchronous process; it’s probably better to handle this with a callback.
For example, couldn’t you use a closeCallback?
function showTargetDirectoryChooser(done){
var win = new Window( 'dirchooser_' + new Date().getTime() , {className: 'alphacube', width: 320, height: 470, url: '/directories/choose', maximizable: false});
win.showCenter();
win.setDestroyOnClose();
// This will ensure
win.setCloseCallback(function () {
done(win.content.contentWindow.Directory);
return true; // or return false if you don't want the window to be closed
});
return true;
}
With this, you would change
var chosenDir = showTargetDirectoryChooser();
// do something with chosen directory
into
var chosenDir;
showTargetDirectoryChooser(function (directory) {
chosenDir = directory;
// do something with the chosen directory
});

One option would be to use an event handler. When Directory is set via click or perhaps a change event, if that is fired, attach a handler which takes that event and passes it back to a function in your main window. This will require making your code asynchronous - so that you have a caller that calls showTargetDirectoryChooser() and a callback that takes the result of the directory as separate functions. It shouldn't be too complicated to re-architect your code there and break it up to the caller and the callback, though.
You can also use setTimeout and poll the contents of win.content.contentWindow.Directory like so:
function showTargetDirectoryChooser(){
var win = new Window( 'dirchooser_' + new Date().getTime() , {className: 'alphacube', width: 320, height: 470, url: '/directories/choose', maximizable: false});
win.showCenter();
win.setDestroyOnClose();
setTimeout("pollDirectory()", 500); // poll in a half second
}
function pollDirectory() {
if(win.content.contentWindow.Directory != null) {
callback(win.content.contentWindow.Directory); // you will need an asynch callback
} else {
setTimeout("pollDirectory()", 500); // poll in a half second
}
}
Doing it this way also requires that your code be asynchronous.
Another option is to look into jquery wait, but that is a timeout as opposed to waiting on a condition. Or there is a set of reactive extensions to JS, but that looks to be overkill for what you are doing. The concept of an observable may be something you want to look into, though.

Related

How to enable process only once after receiving something from another Bus

I need idea how to achieve something with BaconJS. I need to have something like this solution (it is just an example) but I want to call processBus only once after button is clicked. So I need to call
console.log((new Date()).getTime());
Only when the button is clicked and there is event into globalBus.
Here is my code:
var globalBus = new Bacon.Bus();
var processBus = new Bacon.Bus();
function startProcess() {
globalBus.push(new Bacon.Next());
}
processBus.skipUntil(globalBus).onValue(function() {
console.log((new Date()).getTime());
});
setInterval(function() {
processBus.push(new Bacon.Next());
}, 1000);
and here is the example jsfiddle:
http://jsfiddle.net/Tjdp5/13/
I mean only once but every time after button is clicked.
This is example of what I want to achieve but without BaconJS
http://jsfiddle.net/Tjdp5/14/
If I understood your intention correctly, you're looking for a "task queue" mechanism. Here's my suggestion.
var taskE = Bacon.fromEvent(button1, "click")
// use flatMap to force strict evaluation
.flatMap(function() { return new Date() })
.doLog('task created:')
var processE = Bacon.fromEvent(button2, "click")
taskE
// for each new task, take one "process next task" click
// use flatMapConcat to queue tasks
.flatMapConcat(function(task) { return processE.take(1).map(task) })
.onValue(function(task) {
console.log('task ', task, 'processed at', new Date());
});
Working example here: http://jsfiddle.net/52mb7b36/

How to show progress bar while xmlHttpRequest properly?

The problem is that when I'm trying to show progress bar(just .gif) I don't see it. When execute step by step using chrome dev tools. I see it. When run without breakpoints I don't. It seems like it didn't manage to draw gif before start request but I don't understand why. the code is the following:
startLoadingAnimation();
var bill = model.save(data);
stopLoadingAnimation();
where startLoadingAnimation(); is:
function startLoadingAnimation()
{
var imgObj = $("#loadImg");
imgObj.show();
var centerY = $(window).scrollTop() + ($(window).height() + imgObj.height())/2;
var centerX = $(window).scrollLeft() + ($(window).width() + imgObj.width())/2;
imgObj.offset({top:centerY, left:centerX});
}
and stopLoadingAnimation() is:
function stopLoadingAnimation()
{
$("#loadImg").hide();
}
and model.save(data) finally goes to
SOAPClient._sendSoapRequest(url, method, parameters, async, callback, wsdl, forseArray);
Your best bet is to probably have your save method take a callback function as a second option ... that then gets passed down in to your _sendSoapRequest callback parameter and then you can take advantage of your stopLoadingAnimation function.
Pseudo-code
var bill = model.save(data, { callback: function() { stopLoadingAnimation(); } });
You'll need to modify your existing .save() function to handle that second parameter of options (if it doesn't already accept something).

Chrome App: Cannot retrieve file load status

My Chrome app has a function that asks for a file to be loaded by another function, checks that the function has set a flag signifying success (External.curFile.lodd), then attempts to process it. My problem is that the flags are not set the first time I call the function, but when I call it a second time the flags are already set.
I had a feeling this has to do with Chrome file functions being asynchronous, so I had the first function idle for a bit while the file loads. The first load never succeeds, no matter how long I wait, but the second load always does!
Calling Function:
function load_by_lines_from_cur_dir( fileName, context ){ // determine the 'meaning' of a file line by line, return last 'meaning', otherwise 'null'
var curLineMeaning = null;
var lastLineValid = true;
External.read_file_in_load_path(fileName); // 'External' load 'fileName' and reads lines, REPLacement does not see this file
// This is a dirty workaround that accounts for the fact that 'DirectoryEntry.getFile' is asynchronous, thus pre-parsing checks fail intil loaded
var counter = 0, maxLoops = 10;
nuClock();
do{
sleep(500);
counter++;
preDebug.innerText += '\r\nLoop:' + counter + " , " + time_since_last();
}while( !External.curFile.lodd && (counter < maxLoops) ); //idle and check if file loaded, 5000ms max
preDebug.innerText += '\r\nLoaded?:' + External.curFile.lodd;
preDebug.innerText += '\r\nLines?:' + External.curFile.lins;
if( External.curFile.lodd ){ // The last load operating was successful, attempt to parse and interpret each line
// parse and interpret lines, storing each meaning in 'curLineMeaning', until last line is reached
while(!External.curFile.rEOF){
curLineMeaning = meaning( s( External.readln_from_current_file() ), context);
preDebug.innerText += '\r\nNext Line?: ' + External.curFile.lnnm;
preDebug.innerText += '\r\nEOF?: ' + External.curFile.rEOF;
}
} // else, return 'null'
return curLineMeaning; // return the result of the last form
}
which calls the following:
External.read_file_in_load_path = function(nameStr){ // Read the lines of 'nameStr' into 'External.curFile.lins'
External.curPath.objt.getFile( // call 'DirectoryEntry.getFile' to fetch a file in that directory
nameStr,
{create: false},
function(fileEntry){ // action to perform on the fetched file, success
External.curFile.name = nameStr; // store the file name for later use
External.curFile.objt = fileEntry; // store the 'FileEntry' for later use
External.curFile.objt.file( function(file){ // Returns 'File' object associated with selected file. Use this to read the file's content.
var reader = new FileReader();
reader.onload = function(e){
External.curFile.lodd = true; // File load success
};
reader.onloadend = function(e){
//var contents = e.target.result;
// URL, split string into lines: http://stackoverflow.com/questions/12371970/read-text-file-using-filereader
External.curFile.lins = e.target.result.split('\n'); // split the string result into individual lines
};
reader.readAsText(file);
External.curFile.lnnm = 0; // Set current line to 0 for the newly-loaded file
External.curFile.rEOF = false; // Reset EOF flag
// let's try a message instead of a flag ...
/*chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});*/
} );
},
function(e){ External.curFile.lodd = false; } // There was an error
);
};
This app is a dialect of Scheme. It's important that the app knows that the source file has been loaded or not.
I didn't read through all of your code, but you can't kick off an asynchronous activity and then busy-wait for it to complete, because JavaScript is single threaded. No matter what's happened, the asynchronous function won't be executed until the script completes its current processing. In other words, asynchronous does not imply concurrent.
Generally speaking, if task A is to be performed after asynchronous task B completes, you should execute A from the completion callback for B. That's the straightforward, safe way to do it. Any shortcut, to achieve better responsiveness or to simplify the code, is going to have dependency or race-condition problems, and will require lots of horsing around to get right. Even then, it will be hard to prove that the code operates correctly on all platforms in all circumstances.

How can I detect that new content has been loaded in response to a scroll trigger?

I'm running a script on Facebook that requires me to get the IDs of people in my "friends" window (this might not be the most efficient way to accomplish this specific task, but since I'd like to know how to do this in general it's a good example).
This means that if I have more than a small number of friends I have to scroll down for Facebook to add them to the page.
I've added logic that scrolls the page down to the footer, but I don't know how to force my function that grabs the IDs to run after the content loads.
For now, I've resorted to using setTimeout for a few seconds - obviously, this isn't guaranteed to at the appropriate time, so I'd like to know how to do this properly:
var k;
function doit(){
k = document.getElementsByClassName("_698");
var g= Array.prototype.slice.call(k);
confirm(g.length);
// the confirm is just to make sure it's working
// (if i don't use setTimeout it'll return a smaller number
// since not all the friends were included)
}
window.addEventListener("load", function(){
document.getElementById( "pageFooter" )
.scrollIntoView();setTimeout(doit,3000);
});
Crayon Violent details how to accomplish this in his answer to JavaScript detect an AJAX event. The trick is to hook the underlying XMLHttpRequest object in order to detect when a request is sent.
I've re-written the logic there a bit to make it more suitable for your needs:
//
// Hooks XMLHttpRequest to log all AJAX requests.
// Override ajaxHook.requestCompleted() to do something specific
// in response to a given request.
//
var ajaxHook = (function()
{
// we're using a self-executing function here to avoid polluting the global
// namespace. The hook object is returned to expose just the properties
// needed by client code.
var hook = {
// by default, just logs all requests to the console.
// Can be overridden to do something more interesting.
requestCompleted: function(xmlHttp, url, method) { console.log(url); }
};
// hook open() to store URL and method
var oldOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url)
{
this.hook_method = method;
this.hook_url = url;
oldOpen.apply(this, arguments);
}
// hook send() to allow hooking onreadystatechange
var oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function()
{
var xmlhttp = this;
//hook onreadystatechange event to allow processing results
var oldReadyStateChange = xmlhttp.onreadystatechange;
xmlhttp.onreadystatechange = function()
{
oldReadyStateChange.apply(xmlhttp, arguments);
if ( this.readyState === 4 ) // completed
{
hook.requestCompleted(xmlhttp,
xmlhttp.hook_url, xmlhttp.hook_method);
}
};
oldSend.apply(this, arguments);
};
return hook;
})();
With this bit of code loaded in your userscript, you can then implement your logic as follows:
var k;
function doit()
{
k = document.getElementsByClassName("_698");
var g= Array.prototype.slice.call(k);
confirm(g.length);
}
window.addEventListener("load", function()
{
ajaxHook.requestCompleted = function(xmlhttp, url, method)
{
// is this the request we're interested in?
// (Facebook appears to load friends from a URL that contains this string)
if ( /AllFriendsAppCollectionPagelet/.test(url) )
{
// Facebook defers rendering the results here,
// so we just queue up scraping them until afterwards
setTimeout(doit, 0);
}
};
// trigger loading of more friends by scrolling the bottom into view
document.getElementById( "pageFooter" )
.scrollIntoView();
});

How can I wait for a condition?

I'm new on protractor, and I'm trying to implement an e2e test.
I don't know if this is the right way to do this, but...
The page that I want to test is not a full angular page based, so... I'm having some trouble.
On my first spec I have:
describe('should open contact page', function() {
var ptor = protractor.getInstance();
beforeEach(function(){
var Login = require('./util/Login');
new Login(ptor);
});
I have created this Login class, but after login I want to open the contact page, but protractor immediately try to find element before the page is fully loaded.
I've tried to use:
browser.driver.wait(function() {
expect(browser.findElement(by.xpath("//a[#href='#/contacts']")).isDisplayed());
ptor.findElement(by.xpath("//a[#href='#/contacts']")).click();
});
But it doesn't work... it always try to find the element before the page loads.
I tried this one too:
browser.driver.wait(function() {
expect(ptor.isElementPresent(by.xpath("//a[#href='#/contacts']")));
ptor.findElement(by.xpath("//a[#href='#/contacts']")).click();
});
I'm able to do that using browser.sleep(); but I don't think that is a good option. Any idea? On my login class I have:
ptor.ignoreSynchronization = true;
How can I wait for this #href='#/contacts before protractor tries to click on it?
Protractor 1.7.0 has also introduced a new feature: Expected Conditions.
There are several predefined conditions to explicitly wait for. In case you want to wait for an element to become present:
var EC = protractor.ExpectedConditions;
var e = element(by.id('xyz'));
browser.wait(EC.presenceOf(e), 10000);
expect(e.isPresent()).toBeTruthy();
See also:
Expected conditions in protractor
I finally find out...
var waitLoading = by.css('#loading.loader-state-hidden');
browser.wait(function() {
return ptor.isElementPresent(waitLoading);
}, 8000);
expect(ptor.isElementPresent(waitLoading)).toBeTruthy();
var openContact = by.xpath("//a[#href='#/contacts']");
element(openContact).click();
With this protractor could wait for that element until it loading page disappears.
Thanks for those who tried to help XD.
I had the same problem you were having for the longest time while using protractor. In my e2e test I start in a non angular app, then get into an angular portion, then get back out to a non angular portion. Made things tricky. The key is to understand promises and how they work. Here's some examples of my real world code in a functioning e2e test. Hoping this gives you an idea of how to structure your tests. Probably some bad practice in this code, please feel free to improve upon this, but I know that it works, maybe not the best way.
To get to angular I use
var ptor;
var events = require('events');
var eventEmitter = new events.EventEmitter();
var secondClick = require('./second-click');
beforeEach(function () {
browser.driver.get('http://localhost:8080/');
},10000);
it("should start the test", function () {
describe("starting", function () {
it("should find the link and start the test", function(){
var elementToFind = by.linkText('Start'); //what element we are looking for
browser.driver.isElementPresent(elementToFind).then(function(isPresent){
expect(isPresent).toBe(true); //the test, kind of redundant but it helps pass or fail
browser.driver.findElement(elementToFind).then(function(start){
start.click().then(function(){ //once we've found the element and its on the page click it!! :)
ptor = protractor.getInstance(); //pass down protractor and the events to other files so we can emit events
secondClick(eventEmitter, ptor); //this is your callback to keep going on to other actions or test in another file
});
});
});
});
});
},60000);
While in angular this code works
describe("type in a message ", function(){
it("should find and type in a random message", function(){
var elementToFind = by.css('form textarea.limited');
browser.driver.isElementPresent(elementToFind).then(function(isPresent){
element(elementToFind).sendKeys(randomSentence).then(function(){
console.log("typed in random message");
continueOn();
});
});
});
},15000);
After exiting angular
browser.driver.wait(function(){
console.log("polling for a firstName to appear");
return browser.driver.isElementPresent(by.name('firstName')).then(function(el){
return el === true;
});
}).
then(function(){
somefunctionToExecute()
});
Hope that gives some guidance and helps you out!
browser.driver.wait(function() {
return browser.driver.isElementPresent(by.xpath("//a[#href='#/contacts']"));
});
This works for me too (without the timeout param)..
for more information, see http://angular.github.io/protractor/#/api?view=webdriver.WebDriver.prototype.wait
Thanks to answers above, this was my simplified and updated usage
function waitFor (selector) {
return browser.wait(function () {
return browser.isElementPresent(by.css(selector));
}, 50000);
}
Have you tried putting the ng-app in the <html> tag (assuming this part of code is under your control)? This solved a lot of initialization timing problems for me.
Best way to use wait conditions in protractor that helps to show proper error message to particular element if test case failed
const EC = ExpectedConditions;
const ele = element(by.xpath(your xpath));
return browser.wait(EC.visibilityOf(ele),9000,'element not found').then(() => {
ele.click();
});
I'm surprised that nobody has added this solution. Basically, if you are using modal dialogues you often get an element visible and available to click but not being clickable due to the modal dialogue being in front of it. This happens because protractor moves faster than angular and is ready to click the next element while angular is still closing the modal.
I suggest using
public async clickElementBug(elementLocator: Locator) {
const elem = await element(elementLocator);
await browser.wait(
async function() {
try {
await elem.click();
return true;
} catch (error) {
return false;
}
},
this.TIMEOUT_MILLIS,
'Clicking of element failed: ' + elem
);
}
browser.wait may sound too ordinary, but it's not!
browser.wait is the way to go. Just pass a function to it that would have a condition which to wait for. For example wait until there is no loading animation on the page
let $animation = $$('.loading');
await browser.wait(
async () => (await animation.count()) === 0, // function; if returns true it stops waiting; can wait for anything in the world if you get creative with it
5000, // timeout
`message on timeout` // comment on error
);
Make sure to use await
You can also use existing library called ExpectedConditions that has lots of predefined conditions to wait for
You can't imagine what you can do with it...
A few of my favorite ones:
wait until the number of browser's tab's is 2
// wait until the number of browser's tab's is 2
await browser.wait(
async () => {
let tabCount = await browser.getAllWindowHandles();
return tabCount.length === 2;
},
5000,
'the url didnt open in a new window'
);
wait until the loading animation is gone for at last 750ms
// wait until the loading animation is gone for at last 750ms
await browser.wait(
async () => (await this.$$loadAnimations.count()) === 0 && !(await browser.sleep(750)) && (await this.$$loadAnimations.count()) === 0,
5000,
`waiting timeout`
);
wait for ANY number of elements to be present
// wait for any number of elements to be present
async waitForElements($elem, timeout = 120000, start = +new Date()) {
let conditions = [];
for (let i = 0; i < $elem.length; i++) {
conditions.push(ExpectedConditions.presenceOf($elem[i]));
}
await browser.wait(
ExpectedConditions.and(...conditions),
remainingTimeout(timeout, start),
`wait for all elements`
);
}
// and use
await waitForElements([
$usernameField,
$passwordFiend,
$submitButton
])

Categories

Resources