So what I want to do is create a casperJS function which allows us to repeat a step X times, by refreshing the page first, when this step function reaches the timeout.
For unreliable test due to a specific page bug/freeze at the moment and reduce the percentage of false negative.
I have just a problem, I don't know how to break this loop, because I'm in IIFE scope, see following code :
var echoTest = function(){
casper.echo('Hi');
};
var trueFunction = function(){
return true;
};
var verifyFailedTest = function(number, trueReturn, thenFunction){
var i = 0;
//outer: <-------
for (; i <= number; i++){ // <------ how to break this loop in my function then()
//IIFE
(function(index){
if (index < number-1){
//Execute 'number-1' times the then() step (reload the page each time) if timeout or until trueReturn returns true
casper.then(function(){
casper.waitFor(function checkReturnTrue(){
return trueReturn();
}
, function then() {
thenFunction();
//break outer; break; return false; <------ here where I want to break the loop
}
, function timeout() {
casper.reload();
});
});
}
//last execution, will return the normal error if it fails each time
else if (index === number){
casper.then(function(){
casper.waitFor(function checkReturnTrue(){
return trueReturn();
}
, function then() {
console.log('else');
thenFunction();
});
});
}
else{console.log('verifyFailedTest() bug');}
})(i);
}
};
I tried with label, but I got a syntax error.
Execution :
casper.test.begin('\n*************** Suite of planned test : scenario 1 **************\n', 1, function suite(test) {
casper.start('https://www.google.fr/', function() {
verifyFailedTest(3, trueFunction, echoTest);
});
casper.run(function() {
test.done();
});
});
});
I think can do this without a for loop, by clustering your code into parts and do this recursively:
var verifyFailedTest = function(number, repeatStep, trueReturn){
var index = 0;
function intermediate(){
casper.then(function(){
repeatStep();
casper.waitFor(function checkReturnTrue(){
return trueReturn();
}
, function then() {
this.test.pass("Test passes after " + (index+1) + " try");
}
, function timeout() {
casper.reload();
if (index < number-1) {
intermediate();
} else {
lastTry();
}
index++;
});
});
}
function lastTry(){
casper.then(function(){
repeatStep();
casper.waitFor(function checkReturnTrue(){
return trueReturn();
}
, function then() {
this.test.pass("Test passes after " + (index+1) + " try");
});
});
}
intermediate();
};
You'll have an error only after the number'th try.
But if you want to use your IIFE, the following might work by redefining thenFunction and skipping then block after you know that it is unnecessary (doBreak === true):
var verifyFailedTest = function(number, trueReturn, thenFunction){
var i = 0, doBreak = false;
var oldThenFunction = thenFunction;
thenFunction = function(){
doBreak = true;
oldThenFunction();
};
for (; i <= number; i++){
if (doBreak) {
break;
}
// your IIFE here
(function(index){
if (index < number-1){
//Execute 'number-1' times the then() step (reload the page each time) if timeout or until trueReturn returns true
casper.then(function(){
if (doBreak) { return; }
casper.waitFor(...);
});
}
//last execution, will return the normal error if it fails each time
else if (index === number){
casper.then(function(){
if (doBreak) { return; }
casper.waitFor(...);
});
}
else{console.log('verifyFailedTest() bug');}
})(i);
}
};
Use CasperJS's repeat(int times, Function then) function.
Related
I have a piece of code:
var a = false;
function wait(milliseconds, async) {
if(!async) {
setTimeout(function() {
console.log('Sync timer done.');
a = true;
return true;
}, milliseconds*1000);
}
(...)
f_recipe.forEach(function(item, index) {
if (obj['actual_step'] != 0 && obj['actual_step'] != index ) {
e = "Desync";
throw e;
};
console.log("Step: " + obj.actual_step);
if(item.substr(item.length - 6) != "false)"){
if (eval(item)) {
obj['actual_step']++;
}
} else {
eval(item);
var ival = setInterval(function(){
if(a) {
console.log('do the next thing');
clearInterval(ival);
}
}, 1000);
}
});
But when I get to 'do the next thing'(interval complete), the forEach loop doesn't continue to the next element of the array. 'a' is set to true after timeout (kind of a synchronous wait in JS). f_recipes is a string array with function call (e.g. 'wait(20, false)').
How to get it to work?
What you're trying to do seems like a very bad idea, but promises can help with this (using Bluebird here because it provides Promise.delay and Promise.each):
function wait(seconds, dontActuallyWait) {
return dontActuallyWait ? null : Promise.delay(seconds * 1000);
}
function runSequence(things) {
return Promise.each(things, function(thing) {
return eval(thing);
});
}
runSequence([
'console.log("hello")',
'wait(2, false)',
'console.log("hello again")',
'wait(5, false)',
'console.log("goodbye")'
]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.5.1/bluebird.min.js"></script>
I am using google maps and i am trying to put a pause in execution to prevent QUERY_LIMIT usage issue. My function that plots the addresses looks like this.
The code works, however i want to try setTimeout or setInterval to see if its going to look better on UI.
How do i call it, what should be the first argument?
Thanx alot.
vLocations = [];
for (var i = 0; i < vAddresses.length; i++) {
//pause to prevent OVER_QUERY_LIMIT issue
//geocode "free" usage limit is 5 requests per second
//setTimeout(PlotAddressesAsUnAssigned, 1000);
//sleep(500);
//this will resolve the address and store it in vLocations
AddWaypointAndUnassigned(vAddresses[i]);
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
}
Doing a pause (asynchronous execution) inside a loop (synchronous) will usually result in a lot of trouble.
You can use recursive calls that are done only when a timeout ends.
var vLocations = [];
// Manages the timeout and recursive calls
function AddWaypointAndUnassignedWithPause(index){
setTimeout(function(){
// When the timeout expires, we process the data, and start the next timeout
AddWaypointAndUnassigned(vAddresses[index]);
// Some other code you want to execute
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
if(index < vAddresses.length-1)
AddWaypointAndUnassignedWithPause(++index);
}, 1000);
}
// Start the loop
AddWaypointAndUnassignedWithPause(0);
JSFiddle example.
Try this, hope this will help
vLocations = [];
for (var i = 0; i < vAddresses.length; i++) {
//pause to prevent OVER_QUERY_LIMIT issue
setTimeout(function(){
//this will resolve the address and store it in vLocations
AddWaypointAndUnassigned(vAddresses[i]);
}, 500);
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
}
What about a waiting line, thats fired when an item is added and stopped when there are no items left.
With setTimeout:
var INTERVAL = 1000 / 5;
var to = null;
var vLocations = [];
function addAddress(vAddress) {
vLocations.push(vAddress);
startTimeout();
}
function startTimeout() {
if( to === null ) {
to = setTimout(processLocation, INTERVAL);
}
}
function processLocation() {
if( vLocations.length ) {
var vAddress = vLocations.shift();
AddWaypointAndUnassigned(vAddress);
to = setTimout(processLocation, INTERVAL);
} else {
to = null;
}
}
With setInterval:
var INTERVAL = 1000 / 5;
var to = null;
var vLocations = [];
function addAddress(vAddress) {
vLocations.push(vAddress);
startInterval();
}
function startInterval() {
if( to === null ) {
to = setInterval(processLocation, INTERVAL);
}
}
function processLocation(cb) {
if( vLocations.length ) {
var vAddress = vLocations.shift();
AddWaypointAndUnassigned(vAddress);
} else
clearInterval(to);
to = null;
}
}
I am new to jQuery Promises but I am using them fine in other parts of the app, but this one does not seem to be working as expected. Seems it is not waiting for the return before executing the next code.
function setActivityOnGrid(){
...snip...
var sameActivityCnt = $(actId).length;
if(sameActivityCnt){
sameActivityDialog().then(function(retVal) {
if(retVal == true){
activityid += "-"+sameActivityCnt++;
}
});
}
//code here is being executed before the dialog is even shown
}
function sameActivityDialog(){
var deferred = $.Deferred();
bootbox.confirm("You have already added this activity. Would you like to add it again?", function(result) {
deferred.resolve(result);
});
return deferred.promise();
}
Move the //code here is being executed before the dialog is even shown to here:
var sameActivityCnt = $(actId).length;
if(sameActivityCnt){
sameActivityDialog().then(function(retVal) {
if (retVal === true){
activityid += "-"+sameActivityCnt++;
}
// code goes here!!!!
});
}
Inside the then callback function (or later chained then functions).
Alternatives
var sameActivityCnt = $(actId).length;
sameActivityDialog().then(function(retVal) {
if(sameActivityCnt && retVal === true){
activityid += "-"+sameActivityCnt++;
}
// code goes here!!!!
});
or
var promise = sameActivityDialog();
var sameActivityCnt = $(actId).length;
if(sameActivityCnt){
promise = promise.then(function(retVal) {
if (retVal === true){
activityid += "-"+sameActivityCnt++;
}
return retVal;
});
}
promise = promise.then(function(retVal) {
// code goes here!!!!
return retVal;
});
And if you split this logic into smaller functions then you can actually compose this well:
function doIf(condition, fn) {
return function(data) {
if (condition) {
return fn(data);
} else {
return data
}
};
}
function appendID(retVal) {
if (retVal === true){
activityid += "-"+sameActivityCnt++;
}
return retVal;
}
function doMoreStuff(retVal) {
// code goes here!!!!
return retVal;
}
var sameActivityCnt = $(actId).length;
sameActivityDialog()
.then(doIf(sameActivityCnt, appendID))
.then(doMoreStuff);
The promise is working correctly. A promise doesn't make a non-blocking function block.
It gives you an object which you can repeatedly add new callbacks to which will run when the non-blocking function is done.
I want to return a value inside a setInterval. I just want to execute something with time interval and here's what I've tried:
function git(limit) {
var i = 0;
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
return 'done';
}
i++;
}, 800);
}
var x = git(5);
console.log(x);
And it's not working.
Is there any other way?
What I'm going to do with this is to do an animation for specific time interval. Then when i reached the limit (ex. 5x blink by $().fadeOut().fadeIn()), I want to return a value.
This is the application:
function func_a(limit) {
var i = 0;
var defer = $.Deferred();
var x = setInterval(function () {
$('#output').append('A Running Function ' + i + '<br />');
if (i == limit) {
$('#output').append('A Done Function A:' + i + '<br /><br />');
clearInterval(x);
defer.resolve('B');
}
i++;
}, 500);
return defer;
}
function func_b(limit) {
var c = 0;
var defer = $.Deferred();
var y = setInterval(function () {
$('#output').append('B Running Function ' + c + '<br />');
if (c == limit) {
$('#output').append('B Done Function B:' + c + '<br /><br />');
clearInterval(y);
defer.resolve('A');
}
c++;
}, 500);
return defer;
}
func_a(3).then( func_b(5) ).then( func_a(2) );
This is not functioning well, it should print A,A,A,Done A,B,B,B,B,B,Done B,A,A,Done A but here it is scrambled and seems the defer runs all function not one after the other but simultaneously. That's why I asked this question because I want to return return defer; inside my if...
if (i == limit) {
$('#output').append('A Done Function A:' + i + '<br /><br />');
clearInterval(x);
defer.resolve('B');
// planning to put return here instead below but this is not working
return defer;
}
Do you expect it to wait until the interval ends? That would be a real pain for the runtime, you would block the whole page. Lots of thing in JS are asynchronous these days so you have to use callback, promise or something like that:
function git(limit, callback) {
var i = 0;
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
callback('done');
}
i++;
}, 800);
}
git(5, function (x) {
console.log(x);
});
Using a promise it would look like this:
function git(limit, callback) {
var i = 0;
return new Promise(function (resolve) {
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
resolve('done');
}
i++;
}, 800);
});
}
git(5)
.then(function (x) {
console.log(x);
return new Promise(function (resolve) {
setTimeout(function () { resolve("hello"); }, 1000);
});
})
.then(function (y) {
console.log(y); // "hello" after 1000 milliseconds
});
Edit: Added pseudo-example for promise creation
Edit 2: Using two promises
Edit 3: Fix promise.resolve
Try to get a callback to your git function.
function git(limit,callback) {
var i = 0;
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
callback('done') // now call the callback function with 'done'
}
i++;
}, 800);
}
var x = git(5,console.log); // you passed the function you want to execute in second paramenter
How would I have the h1 change for each iteration of the loop? This code now only displays the h1 text after everything is done.
for (i=0; i<array.length; i++) {
$("body > h1").text("Processing #" + i);
// things that take a while to do
}
Additional info: if I resize the window as it loops, the html updates.
var array = ['one', 'two', 'three']
var i = 0;
var refreshIntervalId = setInterval(function() {
length = array.length;
if (i < (array.length +1)) {
$("h1").text("Processing #" + i);
} else {
clearInterval(refreshIntervalId);
}
i++
}, 1000);
http://jsfiddle.net/3fj9E/
Use a setInterval with a one-millisecond delay:
var i=0, j=array.length;
var iv = setInterval(function() {
$("h1").text("Processing #" + i);
// things that take a while to do
if (++i>=j) clearInterval(iv);
}, 1);
http://jsfiddle.net/mblase75/sP9p7/
Sometimes you can force a render by forcing a recalculation of layout
for (i=0; i<array.length; i++) {
$("body > h1").text("Processing #" + i)
.width(); // force browser to recalculate layout
// things that take a while to do
}
It might not work in all browsers.
A better way, that does not block the browser so much:
function doThings(array) {
var queueWork,
i = -1,
work = function () {
// do work for array[i]
// ...
queueWork();
};
queueWork = function () {
if (++i < array.length) {
$("body > h1").text("Processing #" + i);
setTimeout(work, 0); // yield to browser
}
};
}
doThings(yourArray);
DEMO
I've spent a bit of time working out a jquery function that seems to solve this. Basically, it's a process handler that you can add any number of processes to and then call run to sequentially call these in a asynchronous way.
$.fn.LongProcess = function () {
var _this = this;
this.notifications = [];
this.actions = [];
this.add = function (_notification, _action) {
this.notifications.push(_notification);
this.actions.push(_action);
};
this.run = function () {
if (!_this.actions && !_this.notifications) {
return "Empty";
}
//******************************************************************
//This section makes the actions lag one step behind the notifications.
var notification = null;
if (_this.notifications.length > 0) notification = _this.notifications.shift();
var action = null;
if ((_this.actions.length >= _this.notifications.length + 2) || (_this.actions.length > 0 && _this.notifications.length == 0))
action = _this.actions.shift();
//****************************************************************
if (!action && !notification) {
return "Completed";
}
if (action) action();
if (notification) notification();
setTimeout(_this.run, 1000);
//setTimeout(_this.run,1); //set to 1 after you've entered your actual long running process. The 1000 is there to just show the delay.
}
return this;
};
How to use with <h1 class="processStatus"></h1>:
$(function () {
var process = $().LongProcess();
//process.add(notification function, action function);
process.add(function () {
$(".processStatus").text("process1");
}, function () {
//..long process stuff
alert("long process 1");
});
process.add(function () {
$(".processStatus").text("process2");
}, function () {
//..long process stuff
alert("long process 2");
});
process.add(function () {
$(".processStatus").text("process3");
}, function () {
//..long process stuff
alert("long process 3");
});
process.run();
});
if the process is very long you can use this script which shows every notification for a specific time interval.
here is the code..
html
<div id="ccNotificationBox"></div>
css
#ccNotificationBox{
-webkit-animation-name:;
-webkit-animation-duration:2s;/*Notification duration*/
box-sizing:border-box;
border-radius:16px;
padding:16px;
background-color:rgba(0,0,0,0.7);
top:-100%;
right:16px;
position:fixed;
color:#fff;
}
#ccNotificationBox.active{
-webkit-animation-name:note;
top:16px;
}
#-webkit-keyframes note{
0% {opacity:0;}
20% {opacity:1;}
80% {opacity:1;}
100% {opacity:0;}
}
javascript
var coccoNotification=(function(){
var
nA=[],
nB,
rdy=true;
function nP(a){
nA.push(a);
!rdy||(nR(),rdy=false);
}
function nR(){
nB.innerHTML=nA[0];console.log(nA[0]);
nB.offsetWidth=nB.offsetWidth;//reflow ios
nB.classList.add('active');
}
function nC(){
nB.classList.remove('active');
nB.innerHTML='';
nA.shift();
nA.length>0?nR():(rdy=true);
}
function init(){
nB=document.getElementById('ccNotificationBox');
nB.addEventListener('webkitAnimationEnd',nC,false);
window.removeEventListener('load',init,false);
}
window.addEventListener('load',init,false);
return nP
})();
usage
coccoNotification('notification 1');
example
http://jsfiddle.net/f6dkE/1/
info
the example above is perfect for external js as you use just one global variable which is the name of the function ... in my case coccoNotification
here is a different approach but it does the same
http://jsfiddle.net/ZXL4q/11/