I use callbacks all the time when using 3rd part libraries such as jquery, however I've run into an issue where I need to set up my own call back. Take for instance my current code:
// Get All Rates
function getAllRates() {
$('#shiplist tr.mainRow').each(function() {
var curid = $(this).attr("id");
var cursplit = curid.split("_");
var shipid = cursplit[1];
getRate(shipid);
});
}
This iterates through all the table rows that have class "mainRow", and each main row has the id of "shipment_#####" so for each row it splits by the _ to get the actual ship id, which it then uses to calla "getRate" function which sends an ajax call to the server using UPS or USPS api's to get the rate for the given shipment.
This works fine for UPS all rows start loading at once and return their rates independently. The problem is now with Stamps.com, they use an authenticated soap conversation so an authenticator string needs to be received from the first call and used in the next call and so on.
So basically I need to modify the above function to execute the "getRate()" function and wait for it to complete before executing the next iteration.
Anyone know how I can do this with out a lot of fuss?
EDIT - Clearification on the question being asked:
I want to take the following function:
getRate(shipid);
and access it like so:
getRate(shipid, function(shipList) { _Function_data_here_ });
When I define the getRate function, how do I define it so that it has that callback ability? how does that work?
I would suggest you extract all the IDs at once and pass them to your function. That way, you can easily process the first on individually and the rest afterwards. With deferred objects, this is really easy.
For example:
function extractAllIds(callback) {
var ids = $('#shiplist tr.mainRow').map(function() {
return this.id.split('_')[1];
}).get();
callback(ids);
}
function getRates(ids) {
var first = ids.shift();
$.ajax({data: first, ...}).done(function(response) {
// extract the authenticator string
for(var i = ids.length; i--; ) {
$.ajax({data: ids[i], ...});
}
});
}
extractAllIds(getRates);
In any way, you cannot make $.each wait for response of Ajax calls.
Related
I am using JSON to call a oracle service which fetches data (a list) from Server side. Inside the JSON call, I iterate through the list in a FOR loop and I need to call another web service to fetch some data related to each row in the list. I have written that logic in a JavaScript function, and I am calling that function inside the FOR loop.
The issue I am facing is, for example, if original list consists of a 2 items, then the FOR loop is executing 2 times first, and then the function is called 2 times together.
What I want is that every time the loop is iterated, the java script method should be called in the same iteration, fetch the values, I should be able to perform actions based on the results fetched, and it should be repeated for next iterations.
The javascript code is given below. Sequence of alert I am want is: 1,3,2,4,1,3,2,4,5.
Sequence of alert which I am getting is: 1,4,1,4,3,2,3,2,5
var url = "idcplg?IdcService=AJAX_SERVICE&Id=" + Id;
$.getJSON(escapeCachingForUrl(url), function(data){
for(var i=0;i<data.length;i++){
alert("1");
//some code written
myMethod(data,function(result){
alert("2");
// Need to operate on result
});
alert("4");
}
alert("5");
});
function myMethod(data,callback){
var url = "idcplg?IdcService=AJAX_ANOTHER_SERVICE";
$.getJSON(escapeCachingForUrl(url), {
data : data
}, function(data){
alert("3");
callback(data);
});
}
sorry if this question has been answered before but I couldn't find it.
I have an array of objects, and for each object I want to do an async call (ajax call), and when all async calls are finished, i want to call another function.
eg.
var list = [Object, Object, Object, Object];
var final= [];
$(list).each(function(){
//ajax call
getSomething(data, function(data){
final.push(data);
});
});
After all ajax calls are finished i wanna call function load(final);
Can this be done whith callbacks, and without libraries like when.js etc.
Th.
Call the function when the last item has arrived:
final.push(data);
if (final.length == list.length) load(final);
Since it looks like you have jQuery available, you can use the promises built into jQuery:
var list = [Object, Object, Object, Object];
var promises = [];
$.each(list, function(){
//ajax call
promises.push($.get(data));
});
$.when.apply($, promises).done(function() {
// all ajax calls done
// data from each ajax call is in arguments[0], arguments[1], etc...
load(arguments);
});
One other nice advantage of this mechansim vs. all the others shown so far is that this will keep the results in the order that you requested them, even if they don't come back in that order.
You can also provide a handler to .fail() in addition to .done() (or specify both with a .then(f1, f2)) if you want to catch the case where any ajax call fails.
This is a way to solve the problem with a simple counter
var counter = $(list).length;
$(list).each(function(){
$.get('URL', function(data){
/* do whatever you need for each list item */
if(--counter === 0){
/* Do whatever you wanted to do when all requests completed */
}
});
});
Fundamentally, you keep track of how many calls you've made and how many responses you've gotten, and when you've got all the responses, you call load(final). In your case, quite conveniently you have two arrays and are pushing the results of the calls based on the first array into the second, so you can compare their lengths. (Of course, you'll want to handle the error condition as well.)
Your quoted code is a bit suspect (I think you wanted $.each(list, ..., not $(list).each(...), but I think you probably meant something like this:
var list = [Object, Object, Object, Object];
var final= [];
$.each(list, function(data){
//ajax call
getSomething(data, function(result){ // <= I used `result` rather than `data`; using the same symbol in intermixed code like this is asking for trouble
final.push(result);
if (final.length === list.length) {
// All done
load(final);
}
});
});
I have two JavaScript function in two different files. On click I am calling first function to delete and second function for refresh the text in the div. The problem is delete function is having confirm action. So if I call one after the other refresh function is executing immediately. But I want refresh function to execute after confirmation (Note:delete and refresh JavaScript functions are there in two different projects)
Below is the sample:
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1);
refreshAsseListt();
}
You'll have to use a callback. Any properly-designed library with asynchronous operations (like waiting for the user to confirm an action in an event-driven environment like a browser) should offer a callback for when that operation is complete. You haven't said what library the deleteFileFromTimeline comes from, but hopefully it offers a callback. The callback may be an argument you pass, or it may be part of a "promise" API.
If it accepts a callback directly, that would look something like this:
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1, function() {
refreshAsseListt();
});
}
or
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1, refreshAsseListt);
}
...if your refreshAsseListt (was that supposed to be refreshAssetList?) is compatible with what the library does with the callback (the arguments it passes to it and what it does with the return value).
If it returns a promise instead, that would look something like this:
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1).then(refreshAsseListt);
}
("Promises" are also sometimes called "futures" or "deferred objects.")
if you can change the code of deleteFileFromTimeLine, you can change it to return the result of the confirmation.
and execute refreshAsseListt.
example
function deleteFileFromTimeLine(ID)
{
...
return Confirm('Delete File?');
}
and change your code like this
function deleteIFAsset(a) {
var id1 = a.id;
if(IframeCloudEditor.deleteFileFromTimeline(id1))
{
refreshAsseListt();
}
}
You are searching for a solution, to execute your javascript synchronous. But javascript is always executed synchronously except for special cases like ajax requests or file access.
My advice is to use a callback function to solve this problem.
I'm trying to store all returned objects into one object for later use. I have the loop set in place but when I try to execute some code with the new object, its empty. In this case here alert the object. I guess the alert is executing before the loop is complete. Any way to fix this?
var followers = new Array;
Parse.initialize("xxxxxx", "xxxxx");
var currentUser = Parse.User.current();
var users = JSON.stringify(currentUser);
var user = eval("(" + users + ")");
var listsfollow = user.Follow;
for (var i = 0; i < listsfollow.length; i++) {
var allUsers = Parse.Object.extend("User");
var query = new Parse.Query(allUsers);
query.get(listsfollow[i], {
success: function (results) {
followers.push(results);
},
error: function (object, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and description.
}
});
};
alert(followers);
You are correct and the AJAX stuff in jquery is async, and so the .get() will happen after the alert. The quickest and dirtiest way would be to set it to an async to false before issue the request: http://api.jquery.com/jQuery.ajax/
A better way would be to have your code respond to the result of the call whenever it is ready. This will prevent page blocking and make your code faster.
This is an asynchronous query, and the alert is being shown right after the query has initiated, so it's unlikely that it will be executed after the query has returned any results.
Any code that depends on the query's results should be moved into either the success or error callback functions.
I couldn't figure out how to make it wait for the query but I used this to do something after the last loop iteration. Worked great.
if((--remaining)==0)alert(followers);
I am attempting to create an array of objects and then access object properties within the array, but it comes back undefined. I call the createObjArray() function and immediately after I do a console.log(objArray[1]); and it prints out the object with all it...s properties just fine. However, if I attempt to do console.log(objArray[1].name); firebug prints "undefined". Also, when stepping through my code in firebug I can mouse over objArray[1].name and it displays the correct name. What is happening here, it's driving me nuts.
var objArray = [];
function createObjectArray(numOfObjs) {
for(var i=0; i<numOfObjs; i++) {
packages.push(initObj(i));
}
}
function initObj(i){
var newPackage;
var p = {};
$.getJSON('.../package' + i + '.json', function(data) {
newPackage = new Package(data);
p.name = newPackage.name;
p.id = i;
});
return p;
}
This will work:
var objArray = [];
function createObjectArray(numOfObjs, callback) {
var filledPackage = [];
var nbLeft = numOfObjs;
for(var i=0; i<numOfObjs; i++) {
initObj(i, function(p){
filledPackage.push(p);
nbLeft--;
if (nbLeft === 0){
callback(filledPackage);
}
});
}
}
function initObj(i, callback){
var newPackage;
var p = {};
$.getJSON('.../package' + i + '.json', function(data) {
newPackage = new Package(data);
p.name = newPackage.name;
p.id = i;
callback(p);
});
}
//Get a filled object array:
createObjectArray(5, function(filledArray){
objArray = filledArray;
//Code here will be executed AFTER all the $.getJSON queries have returned.
//objArray is not empty.
});
//Code here will be executed WHILE the getJSON queries are running and
//while objArray is still empty. Due to the way the JS event loop works,
//it is impossible that code placed here will be able to use the content
//of objArray unless you call an async function such as anything AJAX or
//setTimeout, but that's iffy. Code you want to be executed once objArray
//has been filled should be inside of the callback above.
The problem is that $.getJSON is aynchronous, meaning that it doesn't automatically returns a result. Instead, you give it a callback. A callback is a function to execute once it has received a result. In this case, the callback is the anonymous function created when calling $.getJSON. That callback receives the result from the server, adds it to the array and then checks if the array has been filled. Since we're doing async code due to the $.getJSON function, we must return the result asynchronously too. To do so, we demand the initObj function to receive a function to call once it has completed (another callback). We call that callback and pass it the parameter. We then return the filled array through a callback once again.
Your call to $.getJSON is asynchronous. When initObj() returns p it is still an empty object.
However initObj() creates a closure which captures a reference to p so when $.getJSON returns p is populated.
This is why the object seems empty in code you run immediately after populating the array. However by the time you run your console command the asynchronous calls have returned and the objects are populated.
You need to wait for all your async calls to return before continuing work on the array. One way to do this would be to increment a counter when you make a call and decrement it when a call returns, then when the final call returns the counter would drop to zero and you continue processing.
Alternatively you could setup a setTimout loop to keep polling the array the check when all its items are populated.
Both approaches are risky if you think one of the calls might fail, but the approach itself is fundamentally risky as you are making multiple ajax calls so you have to handle multiple possible failures. It would be a lot cleaner to grab all the data in one go so you can handle success / error states once in the success / error handler in jQuery.ajax.