I'm trying to loop through every 1000 elements in a S3 bucket. This is because 1000 elements in the maximum returned by a get request. If there are more than 1000 elements, it get paginated, and the get request returns with a field call IsTruncated as true, and a marker (NextMarker) element to pass to the next call, letting the next get request start at the next 1000 elements.
I'm getting the data from the get request as a parameter in a callback function, and attempting to store the two pieces of above information in global variables for use in an outer loop. However, the outer loop goes off to infinity because the global variables are never modified in my get request callback function. I've tried using window.variable inside the callback to no avail. Could anyone help me restructure this code to accomplish my goals?
Thanks
Outter loop is commented out for debugging purposes. There are a number of debugging console.log statement I used to determined the root of the problem.
<script type="text/javascript">
s3_bucket = "link_to_s3_bucket";
var go = true;
var marker = "";
//while(go){
console.log('pass');
console.log(s3_bucket + marker);
$.get(
s3_bucket+marker,
"{}",
function(data) {
$(data).find('Key').each(function(i, key) {
key = key.innerHTML;
$("<a />", {
href : s3_bucket+key,
text : key
}).prependTo("#links");
$("<br />").prependTo("#links");
});
window.go = $(data).find('IsTruncated')[0].innerHTML;
window.marker = "&marker=" + $(data).find('NextMarker')[0].innerHTML;
},
"xml"
);
//}
console.log(go);
console.log(marker);
</script>
Your data returns asynchronously from Amazon, so those variables haven't been defined yet when you call those console logs. Put the console logs inside of the callback after the variable assignments.
Related
I have an array of links with a url inside them, so I need to call 'get', wait for the response and extract what I want out of it and set an image's src to what I get back
I'm facing a problem where by the time the first 'get' is ready, the loop has finished, so basically every iteration of the loop gets the LAST 'data' response. Basically, only the last iteration of i gets a value, and even that's not always the right answer. It's just the last answer to come back
How do I go about fixing this issue?
for(i of instructors) {
jQuery( document ).ready($.get( i["image-link-post"], function( data ) {
i['image-link'] = data.guid.rendered;
}));
}
Since the loop is dealing with async calls, the iteration do not wait for ajax call to complete, and hence by the time the ajax call received a response, i is pointing to some other item of iteration. In this case, we need to maintain a local variable of iterating item and then pass the local reference to the ajax response handler. Checkout with below refactored code:
// Changed var image to let image
for (i of instructors) {
let image = i;
jQuery(document).ready($.get(image["image-link-post"], function (data) {
image['image-link'] = data.guid.rendered;
}));
}
// Below code also works
for (i of instructors) {
(function (image) {
jQuery(document).ready($.get(image["image-link-post"], function (data) {
image['image-link'] = data.guid.rendered;
}));
}(i));
}
I have a database with different link, I want to go fetch these link and put the inside an array.
I tried with the following code:
var amz=new Array();
function CreaArrayAmazon()
{$.ajax({
url: "php/amazon_affiliate.php",
success: function(data){
var leanamazon = JSON.parse(data);
for (i=0; i<leanamazon.length; i++)
{amz[i]=leanamazon[i].Link
}
}
})
}
I expect to find all the links in the "amz" array because it is a global variable, instead it saves links only when it is inside the AJAX function.
If I insert an "alert" inside the AJAX function (ex. alert(amz[i])) I can correctly see the data, instead if I insert an alert outside that I can't see anything, infact the amz array results to be empity.
Can someone tell me out to take that data out of there?
You might be misunderstanding what is going on here.
AJAX stands for Asynchronous Javascript and XML. Asynchronous means that your code doesn't always run in order.
In this case, your program functions like so../
function CreaArrayAmazon()
{
// Step 1: Make the Call
$.ajax({
url: "php/amazon_affiliate.php",
success: function(data){
// Step 3: When the call succeeds, execute the rest of this inner function.
var leanamazon = JSON.parse(data);
for (i=0; i<leanamazon.length; i++)
{amz[i]=leanamazon[i].Link
}
}
})
// Step 2: Continue Processing....
}
Step 2 happens far before Step 3. By the time your AJAX call finished, Javascript has already finished executing your CreaArrayAmazon call.
Instead, you need to have your inner function (Step 3) call an outside function to react to the new data you've received.
I'm trying to pass a variable inside a Jquery POST method from an object array that I get from an asynchronous IndexedDB call using YDN-DB.
Basically db.values, returns an object array of records stored with information I want to send to a PHP script. I can access the ID or any other field of the record set like r[i].id. The only problem is that I can't access it from the inside of the POST's DONE method so I can delete the record by its ID after it was successfully processed by the PHP script.
Below is what I want to achieve, everything works fine, the only problem is when I try to delete the processed record:
var req = db.values('table');
req.done(function(r){
for(i=0;i<r.length;r++){
var post = $.post('myscript.php', {'sale[]': $.toJSON(r[i])});
post.done(function(data){
if(data == 'ok'){
db.remove('table',r[i].id);
}
});
}
});
Is there a way to do this, and get the ID of the processed record to be deleted instead using its array's position?
Thanks!
The problem is that the closure for the callback function captures the same i variable for all iterations, so when the callsbacks are called the value of i has passed the last item of the array. You can wrap the code in the loop inside a function to create a separate i variable for each iteration:
var req = db.values('table');
req.done(function(r){
for(i=0;i<r.length;r++){
(function(i){
var post = $.post('myscript.php', {'sale[]': $.toJSON(r[i])});
post.done(function(data){
if(data == 'ok'){
db.remove('table',r[i].id);
}
});
})(i);
}
});
For a project of mine I need to do multiple calls to a (remote) API using JSONP for processing the API response. All calls use the same callback function. All the calls are generated dynamically on the client's side using JavaScript.
The problem is as follows: How do I pass additional parameters to that callback function in order to tell the function about the request parameters I used. So, e.g., in the following example, I need the myCallback function to know about id=123.
<script src="http://remote.host.com/api?id=123&jsonp=myCallback"></script>
Is there any way to achieve this without having to create a separate callback function for each of my calls? A vanilla JavaScript solution is preferred.
EDIT:
After the first comments and answers the following points came up:
I do not have any control over the remote server. So adding the parameter to the response is not an option.
I fire up multiple request concurrently, so any variable to store my parameters does not solve the problem.
I know, that I can create multiple callbacks on the fly and assign them. But the question is, whether I can avoid this somehow. This would be my fallback plan, if no other solutions pop up.
Your options are as follows:
Have the server put the ID into the response. This is the cleanest, but often you cannot change the server code.
If you can guarantee that there is never more than one JSONP call involving the ID inflight at once, then you can just stuff the ID value into a global variable and when the callback is called, fetch the id value from the global variable. This is simple, but brittle because if there are every more than one JSONP call involving the ID in process at the same time, they will step on each other and something will not work.
Generate a unique function name for each JSONP call and use a function closure associated with that function name to connect the id to the callback.
Here's an example of the third option.
You can use a closure to keep track of the variable for you, but since you can have multiple JSON calls in flight at the same time, you have to use a dynamically generated globally accessible function name that is unique for each successive JSONP call. It can work like this:
Suppose your function that generate the tag for the JSONP is something like this (you substitute whatever you're using now):
function doJSONP(url, callbackFuncName) {
var fullURL = url + "&" + callbackFuncName;
// generate the script tag here
}
Then, you could have another function outside of it that does this:
// global var
var jsonpCallbacks = {cntr: 0};
function getDataForId(url, id, fn) {
// create a globally unique function name
var name = "fn" + jsonpCallbacks.cntr++;
// put that function in a globally accessible place for JSONP to call
jsonpCallbacks[name] = function() {
// upon success, remove the name
delete jsonpCallbacks[name];
// now call the desired callback internally and pass it the id
var args = Array.prototype.slice.call(arguments);
args.unshift(id);
fn.apply(this, args);
}
doJSONP(url, "jsonpCallbacks." + name);
}
Your main code would call getDataForId() and the callback passed to it would be passed the id value like this followed by whatever other arguments the JSONP had on the function:
getDataForId(123, "http://remote.host.com/api?id=123", function(id, /* other args here*/) {
// you can process the returned data here with id available as the argument
});
There's a easier way.
Append the parameter to your url after '?'. And access it in the callback function as follows.
var url = "yourURL";
url += "?"+"yourparameter";
$.jsonp({
url: url,
cache: true,
callbackParameter: "callback",
callback: "cb",
success: onreceive,
error: function () {
console.log("data error");
}
});
And the call back function as follows
function onreceive(response,temp,k){
var data = k.url.split("?");
alert(data[1]); //gives out your parameter
}
Note: You can append the parameter in a better way in the URL if you already have other parameters in the URL. I have shown a quick dirty solution here.
Since it seems I can't comment, I have to write an answer. I've followed the instructions by jfriend00 for my case but did not receive the actual response from the server in my callback. What I ended up doing was this:
var callbacks = {};
function doJsonCallWithExtraParams(url, id, renderCallBack) {
var safeId = id.replace(/[\.\-]/g, "_");
url = url + "?callback=callbacks." + safeId;
var s = document.createElement("script");
s.setAttribute("type", "text/javascript");
s.setAttribute("src", url);
callbacks[safeId] = function() {
delete callbacks[safeId];
var data = arguments[0];
var node = document.getElementById(id);
if (data && data.status == "200" && data.value) {
renderCallBack(data, node);
}
else {
data.value = "(error)";
renderCallBack(data, node);
}
document.body.removeChild(s);
};
document.body.appendChild(s);
}
Essentially, I compacted goJSONP and getDataForUrl into 1 function which writes the script tag (and removes it later) as well as not use the "unshift" function since that seemed to remove the server's response from the args array. So I just extract the data and call my callback with the arguments available. Another difference here is, I re-use the callback names, I might change that to completely unique names with a counter.
What's missing as of now is timeout handling. I'll probably start a timer and check for existence of the callback function. If it exists it hasn't removed itself so it's a timeout and I can act accordingly.
This is a year old now, but I think jfriend00 was on the right track, although it's simpler than all that - use a closure still, just, when specifying the callback add the param:
http://url.to.some.service?callback=myFunc('optA')
http://url.to.some.service?callback=myFunc('optB')
Then use a closure to pass it through:
function myFunc (opt) {
var myOpt = opt; // will be optA or optB
return function (data) {
if (opt == 'optA') {
// do something with the data
}
else if (opt == 'optB') {
// do something else with the data
}
}
}
I seem to be having some issues with making HEAD requests, and preserving the integrity of data in an array.
Given this snippet:
var imageTemp = Array();
$('*')
.each(function(index){
if($(this).css('background-image') != 'none'){
imageTemp.push($(this).css('background-image').slice(5, -2));
}
});
I capture the URLs of all background-images on a given page. Now, trying to grab the size of each image via HEAD requests for Content-Length, I use this snippet:
var imageData = Array();
for(var i = 0; i < imageTemp.length; i++){
ajaxSizeRequest = $.ajax({
type: "HEAD",
async: true,
url: imageTemp[i],
success: function(message){
imageData.push([imageTemp[i], ajaxSizeRequest.getResponseHeader('Content-Length')]);
}
});
}
However, when I dump imageData via console.log, I each element (which should be an array containing the URL and the content-length) ends up as [undefined, XXXX] where XXXX is always the size of the last requested Content-Length
I'm stumped, though it appears to be a timing/scoping issue. Do I have a sort of race-condition occuring here?
The problem is that the single variables i and ajaxSizeRequest being captured by the callback function are the same variables for all instances of the callback function. I think if you call a function and pass the index variable to it and, at the same time, scope the request variable locally to the function itself use the response parameter of the done handler, you should end up with independent variables captured by the callback. It should then reference each array element and each response variable correctly.
var imageData = Array();
for(var i = 0; i < imageTemp.length; i++){
updateImageData( i );
}
function updateImageData( i )
$.ajax({
type: "HEAD",
async: true,
url: imageTemp[i],
}).done(function(message,text,jqXHR){
imageData.push([imageTemp[i], jqXHR.getResponseHeader('Content-Length')]);
});
}
looks like your i isnt properly closed-in
in addition, you can't use ajaxSizeRequest because it too is pointing to just one request (probably the last, because the loop will execute very fast)
just wrap your success callback function as follows, changing the reference to ajaxSizeRequest:
success: (function(i){
return function(data,status,xhr){
imageData.push([imageTemp[i], xhr.getResponseHeader('Content-Length')]);
};
})(i)
You can scope I like so:
success: function(i){
return function(message){
imageData.push([imageTemp[i], ajaxSizeRequest.getResponseHeader('Content-Length')]);
}
}(i)
You have a single i variable which is shared by all of the callbacks.
Since AJAX is asynchronous, all of the callbacks run after your loop is finished, and they all get the same i.
To fix this, you need to move the AJAX call into a separate function that takes i as a parameter.
Thus, each callback will get a separate i parameter.
If anyone still having trouble with this, and since this post is, like, 5 years-old already, here's a more 'modern' version of the answer: just use let instead of var in the original post's for loop.
Info: Is there any reason to use the “var” keyword in ES6?
and: MDN - Let syntax