I'd like to make subsequent ajax calls in angularJs inside a service.
I've tried with something like this:
var requests = [
{'url': 'call1'},
{'url': 'call2'},
{'url': 'call3'}
];
return $q.all([
$http({
url: requests[0],
method: "POST"
}).then( /*callback*/ ),
$http({
url: requests[1],
method: "POST"
}).then( /*callback*/ )
]);
But this make alla ajax in parallel. I need a way to make this calls subsequent, so after first end, it calls second....
You can chain promises:
var requests =[{'url':'index.html'},
{'url':'index.html'},
{'url':'index.html'}];
function makeCall(n) {
return $http({url:requests[n].url+"?n="+n,method:"GET"}).then(function(r) {
if (n+1<requests.length) return makeCall(n+1);
});
}
makeCall(0);
http://plnkr.co/edit/1HdYUtHKe8WXAFBBq8HE?p=preview
You could use async.eachSeries:
var requests = ['call1', 'call2', 'call3'];
function iterator(request, done) {
$http({
url: request,
method: "POST"
}).then(done);
};
async.eachSeries(
requests,
iterator,
function (err) {
// Done
}
);
From the readme:
eachSeries(arr, iterator, callback)
The same as each only the iterator is applied to each item in the array in series. The next iterator is only called once the current one has completed processing. This means the iterator functions will complete in order.
arr - An array to iterate over.
iterator(item, callback) - A function to apply to each item in the array. The iterator is passed a callback(err) which must be called once it has completed. If no error has occured, the callback should be run without arguments or with an explicit null argument.
callback(err) - A callback which is called after all the iterator functions have finished, or an error has occurred.
You should be able to call another $http call in the "then" callback, returning the return value of $http
$http({...})
.then(function() {
return $http({...});
})
.then(function() {
return $http({...});
});
This works because each call to $http returns a promise. If you return a promise in the "then" success callback, then the next "then" callback in the chain will be deferred until that promise is resolved, which will be when the ajax call completes.
Edit: In response to the comment, you can loop over an array of requests:
var requests = [
{'url':'call1','method':'get'},
{'url':'call2','method':'get'},
{'url':'call3','method':'get'}
];
var promise = null;
angular.forEach(requests, function(details) {
promise = $q.when(promise).then(function() {
return $http(details);
});
});
As in the Plunker at http://plnkr.co/edit/RSMN8WuPOpvdCujtrrZZ?p=preview . The $q.when is just for the first value of the loop, when promise is set to null, so it has a then callback that is called immediately.
Related
What is the use of $q.defer() in following code. I am bit confused or not able to understand the use of $q or defer?
service.serviceCall = function (methodName, params) {
var deferred = $q.defer();
$http({ method: "POST", url: url + methodName, data: params, headers: headers }).success(function (result) {
deferred.resolve(result);
}).error(function (result) {
deferred.reject(result);
});
return deferred.promise;
}
service.serviceCal("POST", {"param1":"value1"}).then(function(data){
//here data will be object which is resolved in success call
}).fail(function(){
//here data will be object which is rejected in failure call
})
This are promises, we user $q.defer() to return a promise using defer.promise(). It is contract between the calling object and promise that in future the calling object (here then and fail) will ultimately get a value either in then or fail depends whether it is resolved or rejected.
Read about promises here: Promises
Go through docs of $q module: $q Module Angular
A service that helps you run functions asynchronously, and use their
return values (or exceptions) when they are done processing.
So promises are used in async programming. $q is Angular's implementation of promises.
Usage of the above function:
service.serviceCal(methodName, params)
.then(function(resolve){
// on successful resolving
// called when defer.resolve is called
// resolved object: resolve
}, function(reject){
// on reject
// called when defer.reject is called
// rejected object: reject
})
The code is using deprecated methods on the result of calling $http. Although $http returns a promise it has some additional methods .success and .error that don't quite work within the usual promise structure.
$q.defer() creates a promise which is returned from the function and this code uses success and error to resolve the promise. A simpler way to write this code would be to just use the promises returned by $http and its .then method.
service.serviceCall = function (methodName, params) {
return $http({ method: "POST", url: url + methodName, data: params, headers: headers })
.then(function successFn (response) {
return response.data;
});
}
This has the same effect as the original code: it returns a promise which either resolves to the data from the response, or if an error occurs the promise is rejected.
I have this code
PedidosService.getProductbyID($scope.listProductos.ProductID).then(function (d) {
$scope.oneProduct = d.data.producto;
});
PedidosService is the name of my factory, getProductbyId is my http get request and $scope.oneProduct is the variable where i want store the result of this request.
factory.getProductbyID = function (id) {
return $http.get('/Pedidos/GetProduct/' + id);
}
factory is my Factory and getProductbyID is my function to call http request
I call this code in a button. The first time that I click in the button, it returns a empty response '[]' but the next times that I click the button, it works fine!!!.
Thanks for you help
As far as I know the $http.get returns a promise because that call is asynchronous. With that in mind a typical call to $http.get should be something like this:
$http.get('/someUrl', config).then(successCallback, errorCallback);
In your context I would have done it this way, so the factory returns a promise and in your controllers you will handle the success and error callback appropriately:
factory.getProductbyID = function (id) {
var deffered = $q.defer();
$http.get('/Pedidos/GetProduct/'+ id)
.then(function (result) {
deffered.resolve(result);
}, function (data) {
deffered.reject(data);
});
return deffered.promise;
}
$scope.tempObject = {};
$http({
method: 'GET',
url: '/myRestUrl'
}).then(function successCallback(response) {
$scope.tempObject = response
console.log("Temp Object in successCallback ", $scope.tempObject);
}, function errorCallback(response) {
});
console.log("Temp Object outside $http ", $scope.tempObject);
I am getting response in successCallback but
not getting $scope.tempObject outside $http. its showing undefined.
How to access response or $scope.tempObject after $http
But if I want to use $scope.tempObject after callback so how can I use it. ?
You need to chain from the httpPromise. Save the httpPromise and return the value to the onFullfilled handler function.
//save httpPromise for chaining
var httpPromise = $http({
method: 'GET',
url: '/myRestUrl'
}).then(function onFulfilledHandler(response) {
$scope.tempObject = response
console.log("Temp Object in successCallback ", $scope.tempObject);
//return object for chaining
return $scope.tempObject;
});
Then outside you chain from the httpPromise.
httpPromise.then (function (tempObject) {
console.log("Temp Object outside $http ", tempObject);
});
For more information, see AngularJS $q Service API Reference -- chaining promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.1
Explaination of Promise-Based Asynchronous Operations
console.log("Part1");
console.log("Part2");
var promise = $http.get(url);
promise.then(function successHandler(response){
console.log("Part3");
});
console.log("Part4");
The console log for "Part4" doesn't have to wait for the data to come back from the server. It executes immediately after the XHR starts. The console log for "Part3" is inside a success handler function that is held by the $q service and invoked after data has arrived from the server and the XHR completes.
Demo
console.log("Part 1");
console.log("Part 2");
var promise = new Promise(r=>r());
promise.then(function() {
console.log("Part 3");
});
console.log("Part *4*");
Additional Resources
Angular execution order with $q
What is the explicit promise construction antipattern and how do I avoid it?
Why are angular $http success/error methods deprecated? Removed from v1.6?
How is javascript asynchronous AND single threaded?
Ninja Squad -- Traps, anti-patterns and tips about AngularJS promisesGood theory but needs to be updated to use .then and .catch methods.
You're Missing the Point of Promises
$http call is async call. The callback function executes when it has returned a response. Meanwhile the rest of the function keeps executing and logs $scope.tempObject as {}.
When the $http is resolved then only $scope.tempObject is set.
Angular will bind the changed value automatically using two way binding.
{{tempObject}} in the view will update itself.
if you want to use tempObject after callback then do this
then(function(data){
onSuccess(data);
},function(){
});
function onSuccess(data){
// do something
}
Try to use a $scope.$apply before to finish the successCallback function. An other solution is to change successCallback -> function so:
$http({ method: 'GET', url: '/myRestUrl' }).then(function(success) { $scope.tempObject = success; console.log("Temp Object in successCallback ", $scope.tempObject); }, function(error) { });
How is result passed from the $http object to the unnamed function that is executed on success?
$http
.success(function (result) {
...
})
I know that the result is passed via any variable name that i put into the function. It is typically called result. But how is this done? It seems like wizardry to me.
I would expect to have to write something like:
$http
.success(function (result=$http.result) {
...
})
You have to study how both Javascript Function Paramters and Promises work.
The code that you pasted comes, I Think, from some AngularJS Application.
If my assumption is correct, $http is a service and doesn't have anyone success method.
The success method is present on $http methods:
//get, post, ecc...
$http.get(...).success()
By the way:
Javascript doesn't provide any way to match parameters, their order is always the order provided by the callee and the names that you use is just for you (Don't confuse with the IOC that the DependencyInjection in AngularJS does). EXAMPLE 1
function loggerCase1(log1, log2, log3, log4) {
console.log('loggerCase1 => param-1:', log1);
console.log('loggerCase1 => param-2:', log2);
console.log('loggerCase1 => param-3:', log3);
console.log('loggerCase1 => param-4:', log4);
console.log('---------------------');
};
function loggerCase2(log4, log2, log1, log3) {
console.log('loggerCase2 => param-1:', log4);
console.log('loggerCase2 => param-2:', log2);
console.log('loggerCase2 => param-3:', log1);
console.log('loggerCase2 => param-4:', log3);
console.log('---------------------');
};
function loggerCaseN() {
for(var i = 0; i < arguments.length; i++) {
console.log('loggerCaseN => param-' + (i + 1) + ': ', arguments[i]);
}
console.log('---------------------');
};
var logs = ['log1', 'log2', 'log3', 'log4'];
loggerCase1.apply(this, logs);
loggerCase2.apply(this, logs);
loggerCaseN.apply(this, logs);
If it's all clear about function parameters behaviour in javascript... you will know that isn't possibile to say give me the first as the second or something like that, also, the example that you pasted seems similar to default parameters (implemented in ES6, aka Javascript Harmony).
Let's go to the point 2:
In a simple promise chain (find on google or see the link above) you can pass a result to the next callback using return. EXAMPLE2
angular
.module('promisechainging', [])
.run(function($q) {
$q
.when('Hello World')
.then(function(greetings) {
console.log('ring 1', greetings);
return greetings;
})
.then(function(salut) {
console.log('ring 2', salut);
return salut;
})
.then(function(ciao) {
console.log('ring 3', ciao);
return { message: ciao };
})
.then(function(result) {
console.log('ring 4', result.message);
return result;
})
.catch(function(error) {
console.log('THIS LOG NEVER HAPPENS BECAUSE THERE AREN\'T REJECTED PROMISES');
return $q.reject(error);
})
.finally(function() {
console.log('We Are At The END');
})
;
})
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="promisechainging"></div>
Basically is not important how parameters are named!
Angular is using the promise mechanism which basically returns an object that let you know when the result is available or an error has been thrown.
When the ajax call returns, angular is calling the promise and providing the result as a parameter.
It's just like calling a regular function.
$http allows you to perform async network operations and returns a promise object (you can read more about promises in Angular here).
The success and error methods were used to declare callbacks to what happens when the promise is resolved (when the request was successfully completed) or rejected (when there was an error at processing the request). I used the past tense since they are now deprecated and the desired way to handle these is using the then method of the promise object.
// Simple GET request example:
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Basically, the syntax is pretty much the same - the successCallbackFunction has the same signature as the method you were passing in the success method of your example.
But this is only the method signature. Your callback function parameters can be called however you want (result, data etc). All you have to keep in mind is that the first parameter in your callback function is going to be the data returned by your request.
$http
.success(function (result) {
...
})
$http will return a Promise Object which is nothing but a Javascript Object with success and different other functions.
So the statement immediately becomes like below as $http is evaluated,
(Promise Object)
.success(function (result) {
...
})
The success function of promise will save this anonymous function to be called once the promise is resolved. We can manually resolve promises, but I guess http will do this for you here.
Once http request(AJAX) is successful angular will tell this Promise object to run this success function by resolving the Promise, somewhat like:
suceess: function(responseData){ //success of AJAX
resolve(responseData); //this will pass the result to promise
}
Once resolve is called promise object has the result with it, it will then call the success function you passed initially with this value of result.
PS: This is a rough idea, I ave to look into Angular source to see their actual implementation.
Javascript functions are also class objects.
When $http completes it will call either the success or fail function - they are objects so they can be passed around. When it does, it will provide the parameters.
I'm pretty new to AngularJS and am learning as I go along. How do I chain successive $http posts? I need reponse data from the first $http POST to use in the second $http POST, of which I'll also need the response which this second POST returns.
$http({
method: 'POST',
url: 'http://yoururl.com/api',
data: '{"field_1": "foo", "field_2": "bar"}',
headers: {'Content-Type': 'application/json'}
}).then(function(resp) {
$scope.data_needed = resp.data_needed;
// Can't possibly do another $http post here using the data I need, AND get its reponse?
// Would lead to a nested relationship, instead of adjacent chaining.
}, function(err) {
// Handle error here.
});
I've found out that it isn't an option to chain another $http post to the last line of code with another .then(function(resp) {});, for the same reason (referring to 1st comment in code block above).
Any advice? All I can seem to find are examples of chaining $http GETs, which do not involve getting and using a response. Cheers.
This is the way to go:
$http({...})
.then(
function success1(response) {
var data = response.data;
$scope.xxx = data.xxx;
return $http({...});
},
function error1(response) {
return $q.reject(response);
}
)
.then(
function success2(response) {
var data = response.data;
$scope.yyy = data.yyy;
},
function error2(response) {
// handle error
}
);
When a then() function returns a promise (the return $http(...) part), the chained then() gets called with the resolved value of the second promise. Also note the return $q.reject(...) part, necessary for the flow to proceed to the second error function, instead of the second success function.