In the code below i need 2 values from the response
Html response of the called page url
The value of the URL which was used to get the page OR the array index which was used in the call.
for (i = 0; i < pageURLs.length; i++) {
$.ajax({
url: pageURLs[i],
dataType: 'html',
statusCode: {
200: function(response) {
/*i am only getting access to the html of the page HERE*/
},
404: function() {
/*404 here*/
}
},
error: function(error) {
}
});}
EDIT Here is a more lightweight way of doing this, using let. See original response for explanation.
Notice This syntax might not be compatible with old browsers... :(
for (i = 0; i < pageURLs.length; i++) {
let j = i;
$.ajax({
url: pageURLs[j],
dataType: 'html',
statusCode: {
200: function(response) {
/* now you can also access j */
console.log("j=", j);
},
404: function() {
/*404 here*/
}
},
error: function(error) {
// error processing
}
});
}
Original answer
You need to wrap your loop body in a function, because the function scope will be preserved within the callback. Thus, you will be able to retrieve the correct i value within your callback.
for (i = 0; i < pageURLs.length; i++) {
(function(i) {
$.ajax({
url: pageURLs[i],
dataType: 'html',
statusCode: {
200: function(response) {
/*i am only getting access to the html of the page HERE*/
/* now you can also access i */
},
404: function() {
/*404 here*/
}
},
error: function(error) {
}});
}(i);
}
Related
I make do with a problem after several tries and I rely on you to find a solution.
What I want to do is to add in a table the number of viewers of a specific streamer via the twitch api.
So I do my ajax call well:
viewerCount(streamer){
let viewers = [];
let streamerList = streamer;
for (let i = streamer.length-1 ; i >=0 ; i--){
$.ajax({
type: 'GET',
url: 'https://api.twitch.tv/helix/streams?user_login='+streamerList[i]+'',
dataType:'JSON',
headers: {
"Client-ID": 'bgbezb2vov7jc4twxauhw3yh30ubbx',
"Authorization": "Bearer "+this.auth
},
success: function(data) {
viewers.push([i, streamerList[i], data['data'][0]['viewer_count']])
},
error: function(data){
console.log(data)
}
})
};
}
Then I push the result in my table and at the end when I do console.log I have the index, the name of the streamer and the number of viewers in my table.
The problem is that before I want to display the result, I'd like all the ajax calls to be completed before displaying. I tested with promise.all but unfortunately I didn't get anywhere.
Thank you in advance for your help.
using the condition below, you will be able to check if all calls are completed: (check code comments):
viewerCount(streamer){
let viewers = [];
let streamerList = streamer;
let completedCalls = 0; // init a new variable to base on
for (let i = streamer.length-1 ; i >=0 ; i--){
$.ajax({
type: 'GET',
url: 'https://api.twitch.tv/helix/streams?user_login='+streamerList[i]+'',
dataType:'JSON',
headers: {
"Client-ID": 'bgbezb2vov7jc4twxauhw3yh30ubbx',
"Authorization": "Bearer "+this.auth
},
success: function(data) {
viewers.push([i, streamerList[i], data['data'][0]['viewer_count']]);
completedCalls++; // increase completedCalls
},
complete: function() { // <-- here the new code
if(completedCalls === streamer.length) {
displayYourResult();
}
},
error: function(data){
console.log(data)
}
})
};
}
I have created a jQuery function extending its own object $. This function translate those elements attached to the element this:
$.fn.extend({
translate: function(sourceLang, targetLang) {
if($(this).text().trim().length < 1 || !isNaN(parseInt($(this).text().trim())) || sourceLang == targetLang)
return;
let $function = this;
$($function).each(function() {
let $each = this;
$.ajax({
url: 'https://translate.yandex.net/api/v1.5/tr.json/translate',
method: 'GET',
dataType: 'JSONP',
crossDomain: true,
data: {
key: /* my-secret-key */,
text: $($each).text(),
lang: sourceLang + '-' + targetLang
},
success: function(response) {
try {
if(response.code !== 200)
throw "Response: " + response.code;
$($each).text(response.text[0])
} catch(error) {
console.error('Translation error on element: ', $($function).text());
console.error('Message returned by the server:', error);
}
},
error: function(xhr, status, error) {
console.error('Translation error on element: ', $($function).text());
console.error('Message returned by the server:', xhr.responseText);
}
});
});
}
});
After loading the code I do this:
$(document).ready(function() {
let lang = $('html').attr('lang').split('-')[0];
$('td td:visible').translate(lang, "en");
});
Note: the HTML tag looks like this <html lang="es-ES"> depending on the logged user language.
The issue I have is the table loads after a couple of seconds (since we are not in Production environment they could be more than 30). Therefore the previous code block is not useful.
Note: the <tbody> tag is created when the data is added.
What I have tried is:
1. Create a setInterval() and clearInterval() when the $('td:visible').length is greater than 0:
let iv = setInterval(function() {
let lang = $('html').attr('lang').split('-')[0];
let rows = $('tbody td:visible');
if(rows.length > 0) {
rows.translate(lang, "en");
clearInterval(iv);
}
}, 1000);
2. Set a .delay() before the translation:
let isTranslated = false;
while(!isTranslated) {
let lang = $('html').attr('lang').split('-')[0];
let rows = $('tbody td:visible');
if(rows.length > 0) {
rows.delay(1000).translate(lang, "en");
isTranslated = true;
}
}
The memory consumed by the browser is greater than 200MB. I also tried with $('table').on('DOMSubstreeModified', 'tbody', function() {}) but it didn't work.
So, what approach would you recommend to use this translation plugin on this table after it loads its tbody?
Edit 1:
I have changed my code so I perform less API requests, thanks to the recommendation of #lucifer63:
let $function = this;
let collection = [];
let translation = '';
$(this).each(function() {
collection.push($(this).text());
});
let text = collection.join('::');
$.ajax({
url: 'https://translate.yandex.net/api/v1.5/tr.json/translate',
method: 'GET',
dataType: 'JSONP',
crossDomain: true,
data: {
key: /* my-secret-key */,
text: text,
lang: sourceLang + '-' + targetLang
},
success: function(response) {
try {
if(response.code !== 200) {
throw "Response: " + response.code;
}
translation = response.text[0].split('::');
$($function).each(function() {
$(this).text(translation.shift());
});
} catch(error) {
console.error('Message returned by the server:', error);
}
},
error: function(xhr, status, error) {
console.error('Message returned by the server:', xhr.responseText);
}
});
But still, I need to figure out how to print after data has loaded.
Well... I think I found the answer I was seeking:
$('body').on('DOMNodeInserted', 'table', function() {
$('td:visible').translate('es', 'en');
});
It seems it is working correctly.
I declare two object with name,link and page properties. After receiving data from Twitch API, I add a property status to my objects, which works inside the function, but leaving it I can't access status property anymore. I even tried to set the status property using getApi to return the status as streamer0.status=getApi(); but it does not work either.
var streamer0={name:"freecodecamp"};
streamer0.link='https://api.twitch.tv/kraken/streams/'+streamer0.name; streamer0.page='https://www.twitch.tv/'+streamer0.name;
var streamer1={name:"famasfordemacia"};
streamer1.link='https://api.twitch.tv/kraken/streams/'+streamer1.name;
streamer1.page='https://www.twitch.tv/'+streamer1.name;
var link="";
$(document).ready(function(){
load();
function load(){
for(var i=0;i<2;i++)
{
switch(i){
case 0:{
link=streamer0.link;
getApi(streamer0);
console.log(streamer0.status) //it does not work
break;
}
case 1:{
link=streamer1.link;
getApi(streamer1);
console.log(streamer1.status) //it does not work
break;
}
}
}
function getApi(x){
$.ajax({
type: 'GET',
url: link,
headers: {
'Client-ID': 'xxxxxxxxxxxxx'
},
success: function(data) {
if(data["stream"]==null)
{
x.status="offline";
console.log(x.status)//works
}
else
{
x.status="online";
}
}
});
}
});
You are Using Ajax , its asynchronous , so you have three options :
1 - put all you code inside the success callback which will be a big miss.
function getApi(x) {
$.ajax({
type: 'GET',
url: link,
headers: {
'Client-ID': 'xxxxxxxxxxxxx'
},
success: function (data) {
// ALL YOUR CODE IN HERE
}
});
}
2 - Using a callback function :
function getApi(x, callback) {
$.ajax({
type: 'GET',
url: link,
headers: {
'Client-ID': 'xxxxxxxxxxxxx'
},
success: function (data) {
// PASS YOUR DATA YOU THE CALL BACK
callback(data);
}
});
}
// THEN USE IT IN THE LOAD FUNCTION AS THE FOLLOWING
function load(){
for(var i=0;i<2;i++)
{
switch(i){
case 0:{
link=streamer0.link;
getApi(streamer0,function(data){
console.log(data.status) //it does not work
});
break;
}
case 1:{
link=streamer1.link;
getApi(streamer1,function(data){
console.log(data.status) //it does not work
});
break;
}
}
}
3 - is using Promise (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise).
It is a asynchronous call so you need wait until the call is done,
function getApi(x) {
return $.ajax({
type: 'GET',
url: link,
headers: {
'Client-ID': 'xxxxxxxxxxxxx' }});
case 0:{
link=streamer0.link;
getApi(streamer0).success(function(data){
if(data["stream"]==null)
{
streamer0.status="offline";
console.log(streamer0.status)//works
}
else
{
streamer0.status="online";
}
}
console.log(streamer0.status) //it does not work
});
break;
}
hope It helps you, :)
I'm trying to come up with a resource loader if you will, that will load many remote resources and then execute a final callback (like rendering a DOM based on the retrieve data from these requests).
Here's the function:
var ResourceLoader = function () {
this.requests = new Array();
this.FinalCallback;
this.Add = function (request) {
this.requests.push(request);
};
this.Execute = function() {
for (var x = 0; x < this.requests.length ; x++) {
var success = this.requests[x].success;
//if this is the last of the requests...
if (x == (this.requests.length - 1) && this.FinalCallback) {
$.when($.ajax({
url: this.requests[x].url,
dataType: 'json',
error: this.requests[x].error,
method: 'GET'
}).done(success)).then(this.FinalCallback);
}
else {
$.ajax({
url: this.requests[x].url,
dataType: 'json',
error: this.requests[x].error,
method: 'GET'
}).done(success);
}
}
};
};
And here's how I use it:
var apiUrl = Utilities.Api.GetWebApiUrl();
var loader = new Utilities.ResourceLoader();
loader.Add({
url: apiUrl + 'regions/get',
success: function (results) {
Filters.Regions = results;
}
});
loader.Add({
url: apiUrl + 'currentfactors/get/83167',
success: function (results) {
Filters.NbrEmployees = results;
}
});
loader.Add({
url: apiUrl + 'currentfactors/get/83095',
success: function (results) {
Filters.Industries = results;
}
});
loader.FinalCallback = RenderBody;
loader.Execute();
function RenderBody() {
console.log('render...');
}
Obviously, I'm expecting RenderBody to be executed last. But that's not what happening. What's ironic is that I remember doing something like that before, but lost the code... Looks like I'm having a brainfart here.
As you've tagged with promise - here's a really clean solution that uses Promise.all
this.Execute = function() {
Promise.all(this.requests.map(function(request) {
return $.ajax({
url: request.url,
dataType: 'json',
error: request.error,
method: 'GET'
}).done(request.success);
})).then(this.FinalCallback);
};
or ... using JQuery when
this.Execute = function() {
$.when.apply($, this.requests.map(function(request) {
return $.ajax({
url: request.url,
dataType: 'json',
error: request.error,
method: 'GET'
}).done(request.success);
})).then(this.FinalCallback);
};
Es6 Promise has solutions for your problem, there is no need to reinvent it unless the loading of resource groups is a specific goal to abstract. Set up a Promise object for each resource request, using the constructor to assign the resolve and reject callbacks appropriately for the XHR. Keep a collection (any Iterable will do) of individualPromise.then(individualCallback) results. Your final product is obtained by Promise.all(collectionOfPromises).then(finalCallback).
I want to show <div> on click by submit before sending $.ajax()
my html
<div id="waiting_div"></div>
css
#waiting_div {
position: fixed;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, .8);
z-index: 999;
display: block;
}
js functions
jQuery(document).ready(function() {
jQuery("#waiting_div").hide();
});
function set_response_waiting() {
jQuery("#waiting_div").show();
}
function del_response_waiting() {
jQuery("#waiting_div").hide();
}
and main js
jQuery("#save_changed_prices").click(function(){
set_response_waiting(); <-- showing div here
var len = window.prices.length; //array with data for sending
var i = 0;
for (i = 0; i < len; i++) {
if (window.prices[i].price >= 0) {
jQuery.ajax({
type: 'POST',
url: ajaxurl,
data: {...... },
async: false
}).done(function (data) {
...
}).fail(function () {
...
}).always(function () {
...
});
}
}
del_response_waiting(); <-- hiding div
});
But set_response_waiting() function doesn't show me my "#waiting_div" before sending.
I need to redraw or update the DOM tree before sending. But how?
This don't work too..
jQuery.ajax({
type: 'POST',
url: ajaxurl,
data: {
'action': 'update_price',
'car_id': car_id,
'dep_city_id': window.prices[i].dep,
'arr_city_id': window.prices[i].arr,
'price': window.prices[i].price
},
beforeSend: set_response_waiting(),
async: false
})
Ajax is asynchronous as you are probably aware of, so in your function JS will go straight from set_response_waiting() to del_response_waiting(); AJAX is not performed 'in series here'; try:
jQuery("#save_changed_prices").click(function(){
var len = window.prices.length; //array with data for sending
var i = 0;
for (i = 0; i < len; i++) {
if (window.prices[i].price >= 0) {
jQuery.ajax({
type: 'POST',
url: ajaxurl,
data: {...... },
async: false
}).done(
function (data) {
del_response_waiting(); //<-- hiding div
...
}
).fail(function () {
...
}
).always(set_response_waiting()); //<-- showing div here
}
}
});
Use, Promise.
jQuery ajax is aync method. so your function show and immediately hide.
jQuery("#save_changed_prices").click(function(){
set_response_waiting(); <-- showing div here
var len = window.prices.length; //array with data for sending
var i = 0;
var deferreds = [];
for (i = 0; i < len; i++) {
if (window.prices[i].price >= 0) {
deferreds.push(
jQuery.ajax({
type: 'POST',
url: ajaxurl,
data: {...... },
async: false
}).done(
function (data) {
...
}
).fail(function () {
...
}
).always(function () { ...
});
);
}
}
// pending until all ajax requests done.
$.when.apply(null, deferreds).done(function() {
del_response_waiting(); <-- hiding div
});
});
--- EDIT
jQuery("#save_changed_prices").click(function(){
set_response_waiting(); <-- showing div here
var len = window.prices.length; //array with data for sending
var i = 0;
var deferreds = [];
for (i = 0; i < len; i++) {
var deferred = $.Deferred();
deferreds.push(deferred.promise());
if (window.prices[i].price >= 0) {
jQuery.ajax({
type: 'POST',
url: ajaxurl,
data: {...... },
async: false
}).done(
function (data) {
...
}
).fail(function () {
...
}
).always(function () {
deferred.resolve(); // Resolve here!
...
});
);
}
}
// pending until all ajax requests done.
$.when.apply(null, deferreds).done(function() {
del_response_waiting(); <-- hiding div
});
});
---- EDIT (last)
jQuery("#save_changed_prices").click(function(){
var send_price = function() {
var deferreds = [];
var len = window.prices.length; //array with data for sending
var i = 0;
for (i = 0; i < len; i++) {
if (window.prices[i].price >= 0) {
deferreds.push(
jQuery.ajax({
type: 'POST',
url: 'http://jsonplaceholder.typicode.com/posts',
data: { price : window.prices[i].price },
async: false
}).done(function (data) {
console.log('done', data);
}).fail(function () {
console.error(done, data);
}).always(function () {
})
);
}
}
$.when.apply(null, deferreds).done(function() {
del_response_waiting();
});
}
set_response_waiting();
setTimeout(send_price); // setTimeout for browser redraw screen!!
});
Check jsfiddle for working example. :)
https://jsfiddle.net/yourimiyi/rsu4vo3m/
Like rsn said in first answer it happens but you can't solve it this way.
Here I set 2sec timeouts so you can see how it happens (I changed your code a bit just for testing purpose):
jQuery("#save_changed_prices").click(function() {
$('#waiting_div').show();
setTimeout(function(){
jQuery.ajax({
type: 'POST'
}).done(
function(data) {
$('#waiting_div').html('show after ajax submit');
setTimeout(function(){
$('#waiting_div').hide();
}, 2000);
}
).fail(function() {}).always(function() {});
}, 2000);
});
You can check example of it here https://jsfiddle.net/1e4xscn8/2/