Using $.ajaxStop() to determine when page is finished loading in CasperJS - javascript

So far in my tests written in CasperJS, I've been using waitForSelector() on page-specific elements to determine if a page has fully loaded (including all the async ajax requests). I was hoping to come up with a more standard way of waiting for page load and was wondering if the following was possible?
Inject as clientscript the following (include.js)
$(document).ajaxStop(function() {
// Do something
})
Description of ajaxStop according to jquery api: Register a handler to be called when all Ajax requests have completed.
Define a casper.waitForLoad function that when called would wait for the "something" in above code block
Use the function in several parts of the test.
Also any tips on the // Do Something part would also be appreciated :) I was thinking about using the window.callPhantom feature in phantomJS but I'm reading that it's not officially supported in casperjs.

I would do something like this in include.js:
(function(){
window._allAjaxRequestsHaveStopped = false;
var interval;
$(document).ajaxStop(function() {
if (interval) {
clearInterval(interval);
interval = null;
}
interval = setTimeout(function(){
window._allAjaxRequestsHaveStopped = true;
}, 500);
});
$(document).ajaxStart(function() {
window._allAjaxRequestsHaveStopped = false;
if (interval) {
clearInterval(interval);
interval = null;
}
});
})();
This sets a (hopefully unique) variable to the window object that can be later retrieved. This also waits a little longer incase there is another request after the previous batch ended.
In CasperJS you would probably do something like the following to wait for the change in the request status. This uses adds a new function to the casper object and uses casper.waitFor() internally to check the change.
casper.waitForAjaxStop = function(then, onTimeout, timeout){
return this.waitFor(function(){
return this.evaluate(function(){
return window._allAjaxRequestsHaveStopped;
});
}, then, onTimeout, timeout);
};
And use it like this:
casper.start(url).waitForAjaxStop().then(function(){
// do something
}).run();
or this:
casper.start(url).thenClick(selector).waitForAjaxStop().then(function(){
// do something
}).run();

Related

AngularJS API call inside getter function

I've inherited a project in AngularJS with some structural issues. It uses an API call to fetch some settings from an endpoint but sometimes the application attempts to use these settings before they're loaded resulting in an 'undefined' error.
I attempted to solve this by using a getter. The idea was that when some method accesses the settings object, it would check if they were already loaded in and if not, fetch them from the API.
I've got it working after a while but there's a problem with this idea. When multiple methods access the settings at the same time (when loading the page) the API is being called multiple times because the first call hasn't come back to override the internal _settings yet.
vm._settings = null;
Object.defineProperty(vm,'settings',{get: function(){
if(vm._settings == null){
settings_service.get().$promise.then(function success(response) {
vm._settings = response;
console.log(vm._settings);
console.log("returning in");
return vm._settings;
}, function error(err) {
handle_api_error(ngNotify, err);
});
}
else return vm._settings;
}});
I can't figure out a way to let the other attempts to execute the getter wait until the first call returns and use that result instead. I'm not sure if I should use await, promises or something else. Thanks in advance
You want to debounce these requests. You could use an existing library or just implement your own.
app.factory('debounce', function($timeout) {
return function(callback, interval) {
var timeout = null;
return function() {
$timeout.cancel(timeout);
timeout = $timeout(function () {
callback.apply(this, args);
}, interval);
};
};
});

Run While loop in the background

i have a javascript application Angular based that runs a while loop after a user press a button it runs the loop until we get a certain number then the loop ends. currently am not able to do anything else on the UI when the loop is running is there a way to push this loop to the background so the user can continue other things on the UI.
Use angular watchers for that:
$rootscope.watch(myParam, function () {});
Or use non-blocking setInterval():
var timer = setInterval(function () {
if (myParam == 'what-i-need') {
clearInterval(timer);
}
}, 200);
You can use webworker, as sample code below (the code is not fully functional but it just to give you an idea.
(
function () {
var scope = self;
var condition = false
scope.addEventListener('message', function (event) {
var caller = event.data;
if(caller.name && caller.command && caller.command === 'TERMINATE'){
return;
}
start(caller);
}, false);
function start(){
while (condition) {
if(condition)
scope.postMessage({result:"result"});
}
}
})();
In your angularjs controller or service
if (!worker) {
worker = new $window.Worker("./online-capability-worker.js");
worker.addEventListener('message', onResponse, false);
}
function onResponse(event) {
var response = event.data;
//This is my nuber returned by worker
}
Javascript is synchronous, while event driven systems allow it to mimic an asynchronous language, your code is still running on a single thread. You could use a setTimeout to recursively call a function rather than use the while loop, that way there is time for the thread to do other things in between each iteration.
function incrementNumber(initial, destination){
if(initial < destination){
setTimeout(function(){incrementNumber(initial+1, destination}), 10);
}else{
alert('We found the number!');
}
}
I would like to point out that I agree with the use of setInterval for something as simple as changing a number, but in browsers (to the best of my knowledge still), the interval is kicked off from the time the callback is initially called, which can cause unexpected results if the callback itself has any significant execution time (say, an ajax call).

Force Chrome to check if a $.ajax call has finished?

Good afternoon guys -
Is there a well known way to check if a $.ajax call has finished?
-- FOR INSTANCE --
Let's say I'm using a $.ajax call to load in a large number of leaflet polygons from a .geojson file (asynchronously as a layer group). Under normal circumstances, this happens almost immediately - but the user has elected to load a large job this time around. The user has assumed that the set of polygons has been loaded and attempts to do something with this group of layers prematurely - only to find that nothing happens (the layer group doesn't actually exist yet in the browser).
My intuition (I'm new to web development) is to have some sort of global flag that's set for the dependent algorithm to check. We would set the flag prior to loading the layers, and then change it to some other value in the .done({}) section of the $.ajax call.
-- UPDATE --
I've reworked the code to allow users to choose whether or not they wish to retry the request, and also force the browser to wait some time before retrying the request.
However, I've also found the the issue seems to be with Chrome. Firefox appears to be able to handle the $.ajax.always callback as soon as it finishes (in other words, the $.ajax.always callback will interrupt the regular flow of javascript).
Chrome appears to block the $.ajax.always callback and only lets it execute after all other javascript has finished running (no interrupt).
This particular question stemmed from a debug case that automated user input from within a loop - and since Chrome doesn't call the $.ajax.always callback until the current process is complete, the process isn't marked as completed.
Example Code:
procBox = []; // Global scope, stands for Process Box
function loadPolygons() {
procBox["loadPolygons"] = "running";
$.ajax({
// ajax things here
}).done(function() {
procBox["loadPolygons"] = "done";
}).fail(function() {
// failure stuff here
});
}
function dependentFunction() {
if procBox["loadPolygons"] === "done") {
// The meat of the function (dependentFunction things here)
} else {
// Backup case that allows the browser to retry the request
/* --
* If this fires, the server is still trying to process the
* ajax request. The user will be prompted to retry the request,
* and upon agreement, the function will be called again after
* 1 second passes.
*/
var msg = "Oops! It looks like we're still fetching your "
+ "polygons from the server. Press OK to retry.";
if (confirm(msg)) {
setTimeout(dependentFunction, 1000);
}
}
}
This approach seems to work well in Firefox - the alert() stops JavaScript execution and gives it a chance for the .done({}) callback to occur. But for some reason, the while loop never allows the .done({}) callback to complete in Chrome!
Does anyone know of a better approach for this than using flags or async: false?
I appreciate any answers and knowledge out there!
There are numerous ways to do :
as already sugegsted you can use
since you use jQuery, you can use custom events https://learn.jquery.com/events/introduction-to-custom-events/:
$(document).on("myajax:done", function(){ dependentFunction();})
$.ajax({...}).done(function(){
$(document).trigger("myajax:done")
});
or even global ajax events
https://api.jquery.com/category/ajax/global-ajax-event-handlers/
but really consider why not to do something like
procBox = {onLoadPolygons:dependentFunction}; // Global scope
function loadPolygons() {
$.ajax({
// ajax things here
}).done(function() {
procBox["onLoadPolygons"]();
}).fail(function() {
// failure stuff here
});
}
function dependentFunction() {
alert("Please wait for the polygons to load");
dependentFunctionThings();
}
function dependentFunctionThings(){
// Do dependent function things...
}
UPD:
if you ensist on your structure, and still want to use blocking function
use setInterval to perform check
function dependentFunction() {
var h = setInterval(function() {
if (procBox["loadPolygons"] == "done") {
clearInterval(h);
// Do dependent function things...
}
}, 100);
}
Or wrap it up into a Promise (http://caniuse.com/#feat=promises)
function dependentFunction() {
(new Promise(function(resolve) {
var h = setInterval(function(){
if (procBox["loadPolygons"] == "done") {
clearInterval(h);resolve();
}
}, 100);
})).then(function(){
// Do dependent function things...
});
}
But I still believe that something wrong in your structure
From the docs :
.ajaxComplete()
Whenever an Ajax request completes, jQuery triggers the ajaxComplete event. Any and all handlers that have been registered with the .ajaxComplete() method are executed at this time.
http://api.jquery.com/ajaxcomplete/

Javascript - pause execution until flag becomes true

how can i pause a javascript execution until a flag becomes true?
For Example, i've a xml message like this:
[...]
<action>
<resource update>id</resourceupdate>
</action>
<action>
<event>id1</event>
</action>
<action>
<event>id2</event>
</action>
<action>
<event>id3</event>
</action>
[...]
I wish that the event nodes are processed only after processing node resourceupdate (which requires more time to be served, as it requires the loading of a page):
in javascript to process this message with an iterator (each) i've tried:
$(_response).find('ACTION').each(function() {
if (tagName=="RESOURCEUPDATE") {
ready = false;
//load the resource with selected id in an iframe
} else if (tagName=="EVENT") {
browserLoaded(); //the waiting function
eventhandler(); //consume the event
}
});
the waiting function is:
function browserLoaded() {
if (!ready) {
setTimeout(browserLoaded(),1000);
}
}
and the ready var becomes true when the iframe is loaded:
$(iframe).load(function() {
ready = true;
});
but when execute i'll catch this error:
Maximum call stack size exceeded error
any ideas?
thanks!
It's really a bad idea to use some kind of a flag. You have to use Deferred Pattern. Something like this:
var resources = [];
$(_response).find('ACTION').each(function() {
var deferred = resources.length > 0 ? resources[resources.length - 1] : null;
switch (tagName) {
case "RESOURCEUPDATE":
deferred = $.Deferred();
//load the resource with selected id in an iframe
$(iframe).bind('load', function () {
deferred.resolve(/*specific arg1, arg2, arg3, ...*/)
});
resources.push(deferred);
break;
case "EVENT":
if (deferred) {
deferred.done(function (/*specific arg1, arg2, arg3, ...*/) {
// process event node
});
}
break;
}
});
// clean up deferreds objects when all them will be processed
$.when.apply($, resources).then(function() {
resources.length = 0;
})
P.S.: http://api.jquery.com/category/deferred-object/
The problem is in this function which call itself until the stack is full:
function browserLoaded() {
if (!ready) {
//here you call browserLoaded function instead of passing a reference to the function
setTimeout(browserLoaded() ,1000);
}
}
Your function should look like this:
function browserLoaded() {
if (!ready) {
// note the missing "()"
setTimeout(browserLoaded, 1000);
}
}
This is a terrible design. You don't need the 'waiting' timeout mechanism. If you are loading the pages via jQuery ajax request, make use of the callback functions to continue with your code execution (you can perhaps keep track of the 'current' item being processed and continue with the next). If you are loading iFrames, that's bad design too, you should move to the jQuery ajax way.
One quick hack that you could do is just set up a polling loop: use setInterval to check every once in a while if the variable has been set and clearInterval and continue execution when its time.
Any way, its going to be a pain to do things. Essentially, the only way to tell something in Javascript to run latter is to package it inside a function. After you do this it gets easier though, since you can pass that function around and have the async code call it back when you are done.
For example, your processing might look something like this:
//this is a simple "semaphore" pattern:
var things_to_load_count = 1
var check_if_done(){
things_to_load_count--;
if(things_to_load_count <= 0){
//code to do stuff after you finish loading
}
};
$(_response).find('ACTION').each(function() {
if (tagName=="RESOURCEUPDATE") {
things_to_load_count++;
run_code_to_load_stuff( check_if_done )
//make sure that the run_code_to_load_stuff function
//calls the callback you passed it once its done.
} else if (tagName=="EVENT") {
//process one of the normal nodes
}
});
//this will run the remaining code if we loaded everything
//but will do nothing if we are still waiting.
check_if_done()
Are you sure that the ready variable that set true in the iframe load function is the same as the one that is checked before another settimeout is called. It seems that the one in the iframe load function is a local variable and the other one a global variable.
Or both ready variables are local.

Javascript: Non-blocking way to wait until a condition is true

I have several ASP.NET UpdatePanels, each with an AsyncPostBackTrigger tied to the same button's serverside click event. Since only one UpdatePanel can be doing its thing at a time, I use .get_isInAsyncPostBack() of the PageRequestManager to prevent a user from being able to access another part of the page until the async postback is complete.
Another part of this page needs to dynamically update multiple update panels consecutively. Since the update panels use async triggers, calling __doPostBack("<%=ButtonName.ClientID %>", 'PanelId'); fires asynchonously. Because of this, it will quickly move along to the next iteration of the loop and try to update the next panel. However, the second iteration fails because there is already another update panel doing an async postback.
Ideally, there would be a way to wait until .get_isInAsyncPostBack() returns false without blocking other client activity.
Research has lead me to a lot people with my problem, almost all of whom are advised to use setTimeOut(). I do not thing this will work for me. I don't want to wait for a specified amount of time before executing a function. I simply want my Javascript to wait while another script is running, preferably wait until a specific condition is true.
I understand that many will probably want to suggest that I rethink my model. It's actually not my model, but one that was handed to our development team that is currently a total mess under the hood. Due to time contraints, rewriting the model is not an option. The only option is to make this work. I think that if I had a way to make the client code wait without blocking, my problem would be solved.
There is no such functionality such as wait or sleep in javascript, since it would stop browser from responding.
In your case I would go with something similar to following:
function wait(){
if (!condition){
setTimeout(wait,100);
} else {
// CODE GOES IN HERE
}
}
It's easy to make a mistake when calling setTimeout that will cause the JavaScript call stack to fill up. If your function has parameters, you need to pass those in at the end of the setTimeout parameter list like this:
function wait(param1, param2){
if (!condition){
setTimeout(wait, 100, param1, param2);
} else {
// CODE GOES IN HERE
}
}
If you pass parameters or even include empty () after the name of the function, it will be executed immediately and fill up the stack.
// This is the wrong way to do it!
function wait(param1, param2){
if (!condition){
setTimeout(wait(param1, param2), 100); // you'll get max call stack error if you do this!
} else {
// CODE GOES IN HERE
}
}
I needed to slow down a process and came up with a helpful little method.
const wait = (seconds) =>
new Promise(resolve =>
setTimeout(() => resolve(true), seconds * 1000)
);
And you can use it like this.
const doWork = async() => {
// After 3 seconds do something...
await wait(3);
console.log('work done');
}
This function calls condFunc which should return true when condition is met. When that happens readyFunc is called. checkInterval sets checking rate in milliseconds
var wait = function(condFunc, readyFunc, checkInterval) {
var checkFunc = function() {
if(condFunc()) {
readyFunc();
}
else
{
setTimeout(checkFunc, checkInterval);
}
};
checkFunc();
};
Usage:
wait(
function() { return new Date().getSeconds() == 10; },
function() { console.log("Done"); },
100
);
prints "Done" when current time is 10 seconds after minute

Categories

Resources