I´m running this test with nightwatch and I have a loop inside a loop, first one runs ok but it fails going inside 2nd loop.
I wrote a console.log and returns:
Number of links:[object Object]
This is my code:
What´s the reason for not entering in loop 2?
Thanks in advance.
.execute(
function() {
return document.querySelectorAll('.menuElementsAgregator>li').length
},
function(result) {
total_links = result.value;
console.log("Number of main links:" + total_links);
for (var i = 2; i <= total_links; i++) {
(function (i) {
browser.waitForElementPresent('.menuElementsAgregator', 3000)
.click('.menuElementsAgregator>li:nth-child(' + i + ')>a')
.waitForElementVisible('.menuElementsAgregator>li:nth-child(' + i + ')', 2000)
.execute(
function () {
return document.querySelectorAll('.menuElementsAgregator>li:nth-of-type(' + i + ')>.tsr-nav-second-level .has-sub .clickableTabWithLink').length
},
function(result) {
total_links2 = result.value;
console.log("Number of links:" + total_links2);
for (var j = 2; j <= total_links2 + 1; j++) {
browser.waitUntilElementIsClickable('.menuElementsAgregator>li:nth-child(' + i + ')')
.click('.menuElementsAgregator>li:nth-child(' + i + ')')
.waitForElementPresent('.menuElementsAgregator>li:nth-of-type(' + i + ')>.tsr-nav-second-level>li:nth-of-type(' + j + ').has-sub', 5000)
.click(' .menuElementsAgregator>li:nth-of-type(' + i + ')>.tsr-nav-second-level>li:nth-of-type(' + j + ').has-sub .clickableTabWithLink:first-child')
.pause(1000)
.waitForElementVisible('.games-list', 5000);
}
}
)
})(i);
}
})
The problem is that total_links is not an integer but an object.
Try to replace
console.log("Number of main links:"+ total_links);
with
console.log("Number of main links:", total_links);
That will show you what's inside total_links. If you can't fix it add a comment with the content of total_links
Absolutely this code would not work, because you cant use a callback in a loop like that,your index 'i' in second loop would be equal 'total_links.length' all the time. You can try this:
.execute(
function() {
return document.querySelectorAll('.menuElementsAgregator>li').length
},
function(result) {
total_links = result.value;
console.log("Number of main links:" + total_links);
for (var i = 2; i <= total_links; i++) {
(function(i){ // this is a closure
browser.waitForElementPresent('.menuElementsAgregator', 3000)
.click('.menuElementsAgregator>li:nth-child(' + i + ')>a')
.waitForElementVisible('.menuElementsAgregator>li:nth-child(' + i + ')', 2000)
.execute(
function() {
return document.querySelectorAll('.menuElementsAgregator>li:nth-child(' + i + ')>.tsr-nav-second-level>.has-sub').length
},
function(result2) {
total_links2 = result2.value;
console.log("Number of links:" + total_links2);
for (var j = 0; j <= total_links2; j++) {// i = total_link.length already, before get in to this loop.
browser.waitForElementPresent('.menuElementsAgregator>li:nth-child(' + i + ')>.tsr-nav-second-level>.has-sub:nth(' + j + ')', 3000)
.click('.menuElementsAgregator>li:nth-child(' + i + ')>.tsr-nav-second-level>.has-sub:nth(' + j + ')>a')
.pause(3000)
.waitForElementVisible('.games-list', 5000);
}
}
)
})(i) // close
}
}
)
You should take a look at api .perform() , this will be help a a lot for this kind of work. It made my code cleaner and readable.
Ps: Spend some time to research closure.
Related
I'm a newbie in JS and have a little to no knowledge about asynchronous program and promise. I have a problem in getting result from post PHP as written in this code:
showModalLink = function(d, i) {
$('#myModalLabel').text(d.source.name + ' - ' + d.target.name);
$('#modalJum').text(d.jumlahlelangsama);
var lelang = d.daftarlelangsama.split(", ");
var lelangmodal = [];
var promises = [];
for (var i = 0; i < lelang.length; i++) {
querystring = "select pemenang from lelang where id = " + lelang[i];
console.log(querystring);
var queryobj = {
query: querystring
};
promises.push($.post('indikasi3modal.php', queryobj));
}
Promise.all(promises).then(function(results) {
if (results[i] == d.source.name) {
console.log("1");
lelangmodal.push(lelang[i] + " - dimenangkan oleh " + d.source.name);
console.log(lelangmodal);
}
else if (results[i] == d.target.name) {
console.log("2");
lelangmodal.push(lelang[i] + " - dimenangkan oleh " + d.target.name);
console.log(lelangmodal);
}
else {
console.log("3");
lelangmodal.push(lelang[i]);
console.log(lelangmodal);
}
$('#modalLelang').text(lelangmodal);
$('#myModal').modal('show');
});}
I have no idea why the results[i] return undefined inside then function loop. Any help (or alternative ways to solve this) appreciated. Thanks!
I have no idea why the results[i] return undefined inside then function loop.
Because you don't have a loop in the .then() function. So i has the value it had at the end of the loop that created all the promises, which is the number of promises that were created. But the indexes of results go from 0 to i-1.
Promise.all(promises).then(function(results) {
for (var i = 0; i < results.length; i++) {
if (results[i] == d.source.name) {
console.log("1");
lelangmodal.push(lelang[i] + " - dimenangkan oleh " + d.source.name);
console.log(lelangmodal);
} else if (results[i] == d.target.name) {
console.log("2");
lelangmodal.push(lelang[i] + " - dimenangkan oleh " + d.target.name);
console.log(lelangmodal);
} else {
console.log("3");
lelangmodal.push(lelang[i]);
console.log(lelangmodal);
}
}
$('#modalLelang').text(lelangmodal);
$('#myModal').modal('show');
});
window.config = {
"Environments": [
"LH5",
"LH8",
"AMS"
],
"Clusters": [
4,
4,
4
]
};
Below is the promise object:
for (var i = 0; i < window.config.Environments.length; i++) {
for (var j = 1; j < window.config.Clusters[i] + 1; j++) {
promiseObj.physical[window.config.Environments[i] + "#Cluster" + j] = $http.get('http://url0/search?idc=' + window.config.Environments[i] + '&type=Physical&cluster=' + j).success(function(data) {
$scope.servers = data; // get data from json
countcores[window.config.Environments[i] + "#Cluster" + j] = 0;
countmemory[window.config.Environments[i] + "#Cluster" + j] = 0;
angular.forEach($scope.servers, function(item) {
countcores[window.config.Environments[i] + "#Cluster" + j] = parseInt(countcores[window.config.Environments[i] + "#Cluster" + j]) + parseInt(item.cores);
countmemory[window.config.Environments[i] + "#Cluster" + j] = parseInt(countmemory[window.config.Environments[i] + "#Cluster" + j]) + parseInt(item.memory);
});
});
promiseObj.virtual[window.config.Environments[i] + "#Cluster" + j] = $http.get('http://url/search?idc=' + window.config.Environments[i] + '&type=Virtual&cluster=' + j).success(function(data) {
$scope.servers = data; // get data from json
countvirtualcores[window.config.Environments[i] + "#Cluster" + j] = 0;
countvirtualmemory[window.config.Environments[i] + "#Cluster" + j] = 0;
angular.forEach($scope.servers, function(item) {
countvirtualcores[window.config.Environments[i] + "#Cluster" + j] = parseInt(countvirtualcores[window.config.Environments[i] + "#Cluster" + j]) + parseInt(item.cores);
countvirtualmemory[window.config.Environments[i] + "#Cluster" + j] = parseInt(countvirtualmemory[window.config.Environments[i] + "#Cluster" + j]) + parseInt(item.memory);
});
});
}
}
What appears to be happenning is that the loop is going quicker than the promise object and j is reaching 5 before the promise object and what is logged is
["undefined#Cluster5"] = 1280
What I'm expecting is
["LH5#Cluster1"] = somevalue;
["LH5#Cluster2"] = somevalue;
["LH5#Cluster3"] = somevalue;
["LH5#Cluster4"] = somevalue;
["LH8#Cluster1"] = somevalue;
["LH8#Cluster2"] = somevalue;
["LH8#Cluster3"] = somevalue;
["LH8#Cluster4"] = somevalue;
["AMS#Cluster1"] = somevalue;
["AMS#Cluster2"] = somevalue;
["AMS#Cluster3"] = somevalue;
["AMS#Cluster4"] = somevalue;
I have multiple promiseObjectives running in the same loop(s) - how does this work? I am aware of JavaScript closure inside loops – simple practical example - However this does not help me, I require further assistance.
The referenced URL was made to exemplify what you need to do and in a simple manner. The issue you are encountering is the same issue as the one referenced in the URL. You need to ensure that you are scoping your i & j variables for each function call. Therefore, you need closures around your success function that pass in the i & j variables to return the function that you wish to call when the success() is called for the $http.get() function.
I am not going to do the exact code but it would look somewhat similar to this:
$http.get('http://url').success(function(i,j){
return function (data){
//code stuff happens here
}
}(i,j));
This way the function(i,j){ ... }(i,j) is called and it then returns the actual function return function(data){} with the i & j variables properly scoped and having the values that they did when the function function(i,j){ ... }(i,j) was called.
**As a side note I would change the scoped varaible names from i & j to something more descriptive and less confusing
I have the following loop for a series of similar click events in jQuery. Everything works fine except for the last line below with the comment. How can I make each loop iteration call these functions respectively: step1(), step2(), step3(), etc.?
for (i = 0; i < 7; i++) {
$("#stepbox" + i).click(function(){
$("#step" + i).show();
$("#stepswrapper section").not("#step" + i).hide();
$("#stepbox" + i).addClass("stepboxactive");
$("#stepboxmain div").not("#stepbox" + i).removeClass("stepboxactive");
step + i(); // I'm stuck here. This doesn't work to create the function calls step1(), step2(), etc.
});
}
Assuming your functions are defined in the global context:
for (i = 0; i < 7; i++) {
$("#stepbox" + i).click(function(){
$("#step" + i).show();
$("#stepswrapper section").not("#step" + i).hide();
$("#stepbox" + i).addClass("stepboxactive");
$("#stepboxmain div").not("#stepbox" + i).removeClass("stepboxactive");
window["step" + i]();
});
}
make an array of the step functions.
var step = [
function () { /* step 0 */ },
function () { /* step 1 */ },
function () { /* step 2 */ }
// ...
];
for (i = 0; i < 7; i++) {
$("#stepbox" + i).click(function(){
$("#step" + i).show();
$("#stepswrapper section").not("#step" + i).hide();
$("#stepbox" + i).addClass("stepboxactive");
$("#stepboxmain div").not("#stepbox" + i).removeClass("stepboxactive");
step[i]();
});
}
I've a for-loop and it won't pass it's i variable or any kind of a variable into Jquery function coded to run with every loop.
for (var i = 0; i < result.length; i++) {
$('#frame-' + i + '').fadeOut(function () {
ALERT(i);
document.getElementById('frame-' + i + '').getElementsByTagName('img')[0].src = 'img/' + result.cat[i].id + '.png';
});
$('#frame-' + i + '').fadeIn();
}
I found that I can use .on or .bind functions but I've no idea how it should be done with fadeOut().
fadeOut.on() won't work.
Any suggestions to get this working?
The problem is by the time your fadeOut completes, the for loop has already completed and as such, the value of i that the fade code is reading, will always be the same (the last value).
Create a closure to pass the value of i. This gives you a local copy of i which won't be overwritten by the for loop. Also change ALERT() to alert() (Javascript is case sensitive).
for (var i = 0; i < result.length; i++) {
(function(i){
$('#frame-' + i).fadeOut(function () {
alert(i);
document.getElementById('frame-' + i + '').getElementsByTagName('img')[0].src = 'img/' + result.cat[i].id + '.png';
}).fadeIn();
})(i);
}
Closure again!!!
$.each(result, function (i, res) {
$('#frame-' + i + '').fadeOut(function () {
alert(i);
$(this).find('img').get(0).attr('src', 'img/' + res.cat.id + '.png');
}).fadeIn();
})
I am trying to insert around 58000 rows of a query inside a string. But after the row around 8000 I get a timeout error.
I've already tried to use SetTimeout funcions but it was of no use.
Check the code that I am working on:
function onQuerySuccess(tx, results) {
console.log("Entering onQuerySuccess");
if(results.rows) {
console.log("Rows: " + results.rows.length);
var len = results.rows.length;
if(len > 0) {
store_html(results, 0);
console.log("Finished Reading Rows: " + len);
saveNotes();
console.log("Finished Saving Notes");
} else {
//This should never happen
console.log("No rows.");
}
} else {
alert("No records match selection criteria.");
}
console.log("Leaving openView");
function store_html(results, rows_complete){
rows_complete=store_html_input(results, rows_complete);
console.log("Returning row:" + rows_complete);
if (rows_complete<results.rows.length)
{
setTimeout(store_html(results, rows_complete), 50);
}
}
function store_html_input(results, rows_complete){
for(var i = rows_complete; i < rows_complete+100; i++) {
gpsTextFile = gpsTextFile + results.rows.item(i).section + ' ' + results.rows.item(i).timestamp + ' ' + results.rows.item(i).latitude + ' ' +
results.rows.item(i).longitude + ' ' + results.rows.item(i).acx + ' ' + results.rows.item(i).acy + ' ' +
results.rows.item(i).acz + ' ' + results.rows.item(i).speed;
gpsTextFile = gpsTextFile + "\n\r";
}
return i;
}
So.. I get that "Javascript execution exceeded timeout".
Thank you for any of your help!
Best Regards.
You need to change your setTimeout() to NOT execute the function immediately. Change from this:
setTimeout(store_html(results, rows_complete), 50);
to this:
setTimeout(function() {store_html(results, rows_complete)}, 50);
As you had it before, it was immediately executing store_html(results, rows_complete) and passing the return value from that to `setTimeout() which was not delaying anything. This is a common mistake (2nd one of these problems I've answered today).