I have an array of url and the requirement is that I have to make http.get requests in a synchronous manner. Only after the first url call is successful, the second one should be called
for(var i in urlArray)
{
/*do some operations and get the next url*/
$scope.alpha(newURL);
}
$scope.alpha = function (newURL) {
$http.get(newURL) // these calls should be synchronous
.success(function () {
})
.error(function () {
});
}
How do I do this?
It seems what you really want to is make the calls sequentially, not necessarily synchronously.
In that case, don't use a loop (because it's synchronous). Simply make the next call in response to the previous call.
Simplified example:
var i = 0;
makeRequest(urlArray[i], function success() {
var nextURL = urlArray[++i];
if (nextURL) {
makeRequest(nextURL, success);
}
});
where makeRequest is the function that makes the Ajax request and calls the callback on success:
function makeRequest(url, callback) {
$http.get(url).success(callback);
}
I am assuming that you want call them sequentially, in that case, you can use something like a recursion, call the function inside the .success callback
var currentURL; // calculate teh currentURL
$scope.alpha(currentURL);
$scope.alpha = function (newURL) {
$http.get(newURL) // these calls should be synchronous
.success(function (response, status, headers, config) {
//get the response
//generate the new currentURL as per your need
//keep a break condition, to exit
$scope.alpha(currentURL);
})
.error(function () {
});
}
2) alternately you can you $q, deferred calls to achieve this
Hope this helps
Related
Within my code I have a function that depends on the result of an async call to an API endpoint. In order for the function to execute properly it needs to wait for the result of the call. So I've read up on async calls and from another Stack Overflow question I read that you should make use of callback functions to enable correct execution.
The code below is my attempt to make use of callbacks to allow my function to run successfully, but it doesn't work and at the moment I think the calls are messed up.
I'm not sure how I need to structure this code, but I first need the getInstructionType() call to return its value, then the GetValidationResult() call to return its value, and then the setValidationRowColor() function needs to execute.
getInstructionType(applicationNumber, function(result) {
getInstructionValidationResult(applicationNumber, function(type) {
setValidationRowColor(result);
});
});
function getInstructionValidationResult(applicationNumber) {
var url = //-snip-;
$.get(url, function(data) {
return data;
});
}
function getInstructionType(applicationNumber) {
var url = //-snip-;
$.get(url, function(data) {
return data;
});
}
You could add arguments to the functions which you can use as callbacks. Then you can call those when the AJAX request completes, something like this:
getInstructionType(applicationNumber, function(result) {
getInstructionValidationResult(applicationNumber, function(type) {
setValidationRowColor(result);
});
});
function getInstructionValidationResult(applicationNumber, callback) {
$.get(/*-snip-*/, function(data) {
// some custom logic to work with the response here...
callback && callback(data);
});
}
function getInstructionType(applicationNumber, callback) {
$.get(/*-snip-*/, function(data) {
// some custom logic to work with the response here...
callback && callback(data);
});
}
The alternative to callbacks (which are completely valid) are promises - which is really just another form or callbacks. Assuming you are using jQuery's $.get, you are already making use of Promises:
getInstructionType(applicationNumber, function(result) {
return getInstructionValidationResult(applicationNumber)
.then(function() {
setValidationRowColor(result)
})
});
function getInstructionValidationResult(applicationNumber) {
var url = //-snip-;
return $.get(url)
}
function getInstructionType(applicationNumber) {
var url = //-snip-;
return $.get(url)
}
Note that all I did was return the $.get and added a .then which accepts your callback inside getInstructionType
I'm probably missing the point somewhere here so I'm looking for advice.
I have a nodejs server which is listening for client connections and, based on the data received, makes calls to an API.
The very first call to that API gets an ID which needs to be used on subsequent calls to group them together.
Where I'm struggling is that the call to the API is necessarily asynchronous and in the callback I'm assigning the ID to a variable. While that async call is being processed by the API server, more data is coming in from the client and needs more API calls made BUT I can't fire them until I know the results from the first call as the second calls depend on it.
What's the proper way to handle this? I feel like I should be using Q to promise the results of the first API call to the second, but I'm not sure how it should be structured. Or should I just be queueing up the API calls until the first completes? How would I do that?
Example problem code :
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
//do some stuff...
firstAPICall();
conn.on('data', handleData);
}
handleData(data) {
//do some stuff...
otherAPIcall();
}
firstAPICall() {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
}
}
}
otherAPICall() {
//How do I make sure I actually have a value
//in conn.myID from the first function???
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
}
}
}
Yes, you should be using promises for this. Make a promise for the id that is asynchronously resolved from the first call, and then use it in the subsequent calls:
handleConnection(conn) {
//do some stuff...
var idPromise = firstAPICall();
conn.on('data', function handleData(data) {
//do some stuff...
otherAPIcall(idPromise).then(function(result) {
…
});
});
}
firstAPICall() {
return Q.Promise(function(resolve, reject) {
client.get("http://myAPI/getID", function (data, response) {
resolve(data[0].myID);
});
});
}
otherAPICall(idPromise) {
return idPromise.then(function(myID) {
return new Promise(function(resolve, reject) {
client.post("http://myAPI/storeData", {
data: {myID:myID, data:someData}
}, function (data, response) {
//do some stuff...
resolve(…);
});
});
});
}
Probably you should factor out creating a promise for the result of a client.get call in an extra function. Also make sure to handle errors correctly there and call reject with them. If client would use the node callback conventions, Q even has some nice helper functions for that.
Try using promises. Then use 'then' to call the otherAPICall()
I think you can assume they will be sending data immediately after connecting. So you can simplify and just check in otherAPICall if you have an ID, if not, you can just use a callback. Promises or the async/await keywords might make things sort of nicer down the line but aren't required for this.
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
conn.on('data', handleData(connm, data));
}
handleData(conn, data) {
//do some stuff...
otherAPIcall(conn);
}
checkID(conn, cb) {
if (!conn.myID) {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
cb();
});
} else {
cb();
}
}
otherAPICall(conn) {
checkID(conn, function() {
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
});
});
}
promises can chain values and are always resolved after the callback occurs with the returned value,
function async(value) {
var deferred = $q.defer();
var asyncCalculation = value / 2;
deferred.resolve(asyncCalculation);
return deferred.promise;
}
var promise = async(8)
.then(function(x) {
return x+1;
})
.then(function(x) {
return x*2;
})
.then(function(x) {
return x-1;
});
promise.then(function(x) {
console.log(x);
});
This value passes through all the success callbacks and so the value 9 is logged ((8 / 2 + 1) * 2 - 1).
I have some JavaScript code which pulls data out of 2 sources.
1st source is local web storage
2nd source is AJAX request.
The condition is simple :
function getMyData(){
if (window.localStorage['myData'] != null)
{
return window.localStorage['myData'];
}
else
{
networkTools.ajax("geyMyData", function (data)
{
return data;
})
}}
But the problem is that AJAX is async process and I don't want the code to continue until getMyData() return something.
I know that I can use callbacks, but would like just to wait until this function returns something and then continue the execution. (Not only for this case, but for general knowledge.)
Is that possible?
Use callback
function getMyData(callback){
if (window.localStorage['myData'] != null)
{
callback(window.localStorage['myData'];)
}
else
{
networkTools.ajax("geyMyData", function (data)
{
callback(data);
})
}
}
How to call getMyData
getMyData(function(data){
//your code
});
Or you can make your ajax request as a synchronous request.
The best way to do this is with a callback function.
You don't want your JavaScript to block for things like network requests because your web page would become completely unresponsive.
This is how you would do it:
function getMyData(callback){
if (window.localStorage['myData'] != null)
{
return window.localStorage['myData'];
}
else
{
networkTools.ajax("geyMyData", function (data)
{
callback(data);
});
}
}
The best option would be to call getMyData with a callback and break your functions up into caller -> getMyData -> callback. it is possible to do a synchronous request but you'd better not because it locks up the browser until the call is done.
function gotData(data){
console.log("got data:",data);
}
function someFunction(){
...
getMyData(gotData);
}
function getMyData(callback){
if (window.localStorage['myData'] != null)
{
callback(window.localStorage['myData']);
}
else
{
networkTools.ajax("geyMyData", function (data)
{
callback(data);
})
}}
I did not include how to do a synchronous request because it's bad to do so but if you really want to do it than how to it can be found anywhere (like MDN developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest)
Grateful for any insight into what I'm misunderstanding here. My requirement is as follows:
I have an array of URLs. I want to fire off an AJAX request for each URL simultaneously, and as soon as the first request completes, call the first callback. Then, if and when the second request completes, call that callback, and so on.
Option 1:
for (var i = 0; i < myUrlArray.length; i++) {
$.ajax({
url: myUrlArray[i]
}).done(function(response) {
// Do something with response
});
}
Obviously this doesn't work, as there is no guarantee the responses will complete in the correct order.
Option 2:
var promises = [];
for (var i = 0; i < myUrlArray.length; i++) {
promises.push($.ajax({
url: myUrlArray[i]
}));
}
$.when.apply($, promises).then(function() {
// Do something with each response
});
This should work, but the downside is that it waits until all AJAX requests have completed, before firing any of the callbacks.
Ideally, I should be able to call the first callback as soon as it's complete, then chain the second callback to execute whenever that response is received (or immediately if it's already resolved), then the third, and so on.
The array length is completely variable and could contain any number of requests at any given time, so just hard coding the callback chain isn't an option.
My attempt:
var promises = [];
for (var i = 0; i < myUrlArray.length; i++) {
promises.push($.ajax({
url: myUrlArray[i] // Add each AJAX Deferred to the promises array
}));
}
(function handleAJAX() {
var promise;
if (promises.length) {
promise = promises.shift(); // Grab the first one in the stack
promise.then(function(response) { // Set up 'done' callback
// Do something with response
if (promises.length) {
handleAJAX(); // Move onto the next one
}
});
}
}());
The problem is that the callbacks execute in a completely random order! For example, if I add 'home.html', 'page2.html', 'page3.html' to the array, the order of responses won't necessarily be 'home.html', 'page2.html', 'page3.html'.
I'm obviously fundamentally misunderstanding something about the way promises work. Any help gratefully appreciated!
Cheers
EDIT
OK, now I'm even more confused. I made this JSFiddle with one array using Alnitak's answer and another using JoeFletch's answer and neither of them work as I would expect! Can anyone see what is going on here?
EDIT 2
Got it working! Based on JoeFletch's answer below, I adapted the solution as follows:
var i, responseArr = [];
for (i = 0; i < myUrlArray.length; i++) {
responseArr.push('0'); // <-- Add 'unprocessed' flag for each pending request
(function(ii) {
$.ajax({
url: myUrlArray[ii]
}).done(function(response) {
responseArr[ii] = response; // <-- Store response in array
}).fail(function(xhr, status, error) {
responseArr[ii] = 'ERROR';
}).always(function(response) {
for (var iii = 0; iii < responseArr.length; iii++) { // <-- Loop through entire response array from the beginning
if (responseArr[iii] === '0') {
return; // As soon as we hit an 'unprocessed' request, exit loop
}
else if (responseArr[iii] !== 'done') {
$('#target').append(responseArr[iii]); // <-- Do actual callback DOM append stuff
responseArr[iii] = 'done'; // <-- Set 'complete' flag for this request
}
}
});
}(i)); // <-- pass current value of i into closure to encapsulate
}
TL;DR: I don't understand jQuery promises, got it working without them. :)
Don't forget that you don't need to register the callbacks straight away.
I think this would work, the main difference with your code being that I've used .done rather than .then and refactored a few lines.
var promises = myUrlArray.map(function(url) {
return $.ajax({url: url});
});
(function serialize() {
var def = promises.shift();
if (def) {
def.done(function() {
callback.apply(null, arguments);
serialize();
});
}
})();
Here's my attempt at solving this. I updated my answer to include error handling for a failed .ajax call. I also moved some code to the complete method of the .ajax call.
var urlArr = ["url1", "url2"];
var responseArr = [];
for(var i = 0; i < length; i++) {
responseArr.push("0");//0 meaning unprocessed to the DOM
}
$.each(urlArr, function(i, url){
$.ajax({
url: url,
success: function(data){
responseArr[i] = data;
},
error: function (xhr, status, error) {
responseArr[i] = "Failed Response";//enter whatever you want to place here to notify the end user
},
complete: function() {
$.each(responseArr, function(i, element){
if (responseArr[i] == "0") {
return;
}
else if (responseArr[i] != "done")
{
//do something with the response
responseArr[i] = "done";
}
});
}
});
})
Asynchronous requests aren't guaranteed to finish in the same order that they are sent. some may take longer than others depending on server load and the amount of data being transferred.
The only options are either to wait until they are all done, only send one at a time, or just deal with them being called possibly out of order.
I'm trying to do this:
function DelBatch()
{var userInfo = get_cookie("UserInfo");
PageMethods.DeleteBatchJSWM(userInfo, function(result)
{window.location = "BatchOperations.aspx";});
}
But it still runs asynchronously. I need the browser to actually wait until my code-behind is finished executing, then it can be refreshed
There's a listbox loaded with values that were just deleted from the database, they shouldn't be visible. Problem I have is the window location refreshes before the code-behind is executed, and nothing seems like it was deleted to the user.
Call it using jQuery ajax instead? It features an option (async) where you can select sync/async mode: http://api.jquery.com/jQuery.ajax/
This excellent article tells you how best to call PageMethods from jQuery: http://encosia.com/using-jquery-to-directly-call-aspnet-ajax-page-methods/
Essentially, all you will need to do is this:
$.ajax({
type: "POST",
async: false,
url: "yourpage.aspx/DeleteBatchJSWM",
data: "{ put json representation of userInfo here }",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
window.location = "BatchOperations.aspx";
}
});
Look at Crockford's JSON stringify for a json formatting solution.
If you want to avoid using jQuery, a work around would be to use another PageMethod in which you check the status of the operation using the javascript setInterval function. It is a little messy, but it does the job if you want zero jQuery and it mimics the synchronicity you seek. I use it for large operations in which I want to update a progress bar to the client or something. Here would be an example of how you would do this given what code you posted:
function DelBatch()
{
var userInfo = get_cookie("UserInfo");
PageMethods.DeleteBatchJSWM(userInfo, function(result) {window.location = "BatchOperations.aspx";});
var status;
//Check to see if it has completed every second
var myInterval = setInterval(function ()
{
PageMethods.CheckDeleteBatchStatus(OnSuccess);
if (status == "Finished")
{
clearInterval(myInterval);
//Finished Deleting. Call your window refresh here
WindowRefresh();
}
}, 1000);
function OnSuccess(result)
{
status = result;
}
}
Code Behind:
[WebMethod]
public static string CheckDeleteBatchStatus()
{
string status = GetDeleteBatchStatus(); //some function to get the status of your operation
return status;
}
I came across this site:
http://abhijit-j-shetty.blogspot.com/2011/04/aspnet-ajax-calling-pagemethods.html
that had a great method for handling Synchronous PageMethod calls.
The javascript code is as follows:
// Make sure page methods operate synchronously
XMLHttpRequest.prototype.original_open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
async = false;
var eventArgs = Array.prototype.slice.call(arguments);
var q = 0;
return this.original_open.apply(this, eventArgs);
}
// Make a generic WebMethod caller:
function WebMethodCall(FunctionName, callingobj) {
var OnSuccess = function (result, userContext, methodName) {
callingobj.push(result);
}
var OnFailure = function (error, userContext, methodName) {
callingobj.push(error.get_message());
}
PageMethods[FunctionName](OnSuccess, OnFailure);
}
// OK, this is kludgy, but here goes. In order to have a synchronous PageMethod call
// we need an object that persists in the namespace to stuff the result value into (like an array)
// Essentially I'm emulating a ByRef call.
// ThisResult is an empty list. The WebMethodCall function sticks a value into that list.
// The code that makes the PageMethods get called synchronously is in Common.js
// Use the functions
var ThisResult = []; // This must be of a type which persists in the namespace
WebMethodCall('HelloWorld', ThisResult);
return ThisResult[0];
Using jQuery was first recommended back in 2009.
Another (extremely verbose) option is implementing a synchronous WebRequestExecutor as shown here (2007-07-04), and perfected here (2007-10-30). The gist of the technique is to copy the ASP.NET AJAX Sys.Net.XMLHttpExecutor as a new class named Sys.Net.XMLHttpSyncExecutor and change the call to xmlHttpRequest.open to pass false as the last parameter to force synchronous operation.
The synchronous executor can be plugged into all requests using WebRequestManager like this:
Sys.Net.WebRequestManager.set_defaultExecutorType('Sys.Net.XMLHttpSyncExecutor');
or you may want to switch it up per-request just before it is invoked:
Sys.Net.WebRequestManager.add_invokingRequest(function(sender, args) {
if (iFeelLikeRunningThisRequestSynchronously) {
args.get_webRequest().set_executor(new Sys.Net.XMLHttpSyncExecutor());
}});
This discussion is the source for most of these links and a few more.
I wrote this, that lets you call a PageMethod synchronously. It also will just return the result of the method, and throw an error that can be caught in a try/catch block, so you don't need to worry about supplying onSuccess and onError functions.
function synchronusPageMethod(method) {
XMLHttpRequest.prototype.original_open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
async = false;
var eventArgs = Array.prototype.slice.call(arguments);
return this.original_open.apply(this, eventArgs);
};
var result;
var error;
var args = Array.prototype.slice.call(arguments).slice(1);
args.push(function (res) {
result = res;
});
args.push(function (err) {
error = err;
});
method.apply(null, args);
XMLHttpRequest.prototype.open = XMLHttpRequest.prototype.original_open;
if (error !== undefined) {
throw error;
} else {
return result;
}
}
Use it like this:
try {
var result = synchronusPageMethod(PageMethods.myMethod, argument0, argument1);
console.log(result);
} catch(error) {
console.log(error);
}