Chain Promises in angularJS - angularjs

This two functions are part of a controller of a music playlist project i'm working on. After the user selects his files , the prepare function is called to check if each file is valid to play. when done the getIDtags is called to add ID3tags data to each file.
var preparePlaylist = function (files){
var allPromises = [],
currentSongsListLength = $scope.songsList.length || 0;
$.each(files, function (index){
var file = files[index];
file.index = index + currentSongsListLength;
var promiseA = FileValidator2.check(file);
allPromises.push(promiseA);
promiseA
.then (function (file){
SharedService.addSong(file.index, file);
});
});
$q
.all(allPromises)
.then(function () {
console.log('Check Resolved!');
console.log('SharedService updated.');
getID3tags();
});
};
var getID3tags = function (){
var ID3tagsPromises = [];
$.each($scope.songsList, function(index){
var promiseB = ID3tags.getTags($scope.songsList[index]);
ID3tagsPromises.push(promiseB);
promiseB
.then( function (file){
SharedService.addSong(file.index, file);
});
});
$q
.all(ID3tagsPromises)
.then(function () {
console.log('ID3tags Resolved!');
console.log('SharedService updated.');
});
};
How to combine the 2 functions/promises(promiseA, promiseB) into one function with chained promise and still get $q.all when all is done.Thanxs.

So, each function in your file there is to be called one after the other (for this problem domain) then try this.
If functionA returns a promise and functionB returns a promise, functionA's promise can be resolved with the promise from functionB.
function preparePlaylist(){
var finished = $q.defer();
// rest of function body
$q.all(allPromises).then(function(){
//Do whatever you need
finished.resolve(getID3tags());
});
return finished.promise;
function getID3tags(){
var finished = $q.defer();
//rest of function body
$q.all(ID3tagsPromises).then(function(){
//Do whatever you need
finished.resolve('Done both preparePlaylist and getID3tags');
});
return finished.promise;
preparePlaylist.then(function(response){
console.log(response);
});
This might need a little tweaking, but it should work. I've not tested it however. Hope it helps!
Docs for reference: http://docs.angularjs.org/api/ng.$q

Related

Execute $http requests in a particular order

I have about 5 requests that are made when my view loads. This is for an edit form:
var reqA = function() {...};
var reqB = function() {...};
var reqC = function() {...};
var reqD = function() {...};
var reqE = function() {...};
Now I want reqA() and reqB() to load asynchronously and if possible return a single promise.
reqC() and reqD() should only load after reqA() and reqB() have finished and executed their promise.
reqE() should only load after reqC() and reqD().
This is what I want to do, however I have no idea how to go about it. I can load them all asynchronously or one after another by chaining promises but not in the way I want.
If your functions all use the $http provider, it is easy to accomplish. You will want to modify your functions to return the result of the $http call like this
function reqA() {
return $http.get(...);
}
Now that they are returning promises, you can easily use the $q provider to orchestrate your requests:
$q.all([reqA(), reqB()])
.then( () =>
$q.all([reqC(), reqD()])
.then(() => reqE())
);
For regular JS (non ES6):
$q.all([reqA(), reqB()])
.then(function() {
$q.all([reqC, reqD()])
.then(function() {
reqE();
});
});
If you don't want to return the result of $http in your functions, you will need to set them up to return a promise in one way or the other:
function reqA() {
var deferred = $q.defer();
... your request code ...
// call this if everything is ok
deferred.resolve();
// call this if there was an error
deferred.reject();
return deferred.promise;
}
Here is an example that you can take a look at that might make it clearer. It makes use of Promise which is globally available in almost all javascript environments (excluding IE and older node versions).
angular
.module('example', [])
.run(function($q) {
function requestOne() {
return $q.when("one")
}
function requestTwo() {
return $q.when("two")
}
function requestThree() {
return $q.when("three")
}
function requestFour() {
return $q.when("four")
}
function requestFive() {
return $q.when("five")
}
$q
.all([requestOne(), requestTwo()])
.then(function(responses){
console.log(responses[0] === "one");
console.log(responses[1] === "two");
return $q.all([requestThree(), requestFour()]);
})
.then(function(nextResponses){
console.log(nextResponses[0] === "three");
console.log(nextResponses[1] === "four")
return requestFive();
})
.then(function(lastResponse){
console.log(lastResponse === "five")
})
});
angular.element(document).ready(function() {
angular.bootstrap(document, [ 'example' ]);
});
I assume that you are using angular 1, if you aren't there Promise is available in the global scope in almost all environments.

Angular Promises returning q.all( ) with empty array

What am i trying to achieve is as such:
Invoking my service to retrieve all appointments in appointment types (number of types not fixed) tied to a doctor
If there are 3 appointment types, then there will be 3 async calls made
return a single promise with $q.all() after all 3 promises have been resolved
appointmentService
this.getAllDoctorAppointments = function (doctor, appointmentTypeArray) {
var promises = [];
angular.forEach(appointmentTypeArray, function (appointmentType) {
var defer = $q.defer();
$http.get('/appointments/?doctorName=' + doctor + '&apptType=' + appointmentType)
.success(function (listOfAppointments) {
defer.resolve(listOfAppointments);
promises.push(defer.promise);
});
});
return $q.all(promises);
};
In my console log, the appointmentType returns [ ].
This happens because the empty 'promises' array is returned even before the 3 async calls are made. I am still very new to the concept of promises, what is the best approach to work this out? Thanks!
$scope.getAllDoctorAppointments = function (doctor, appointmentTypeArray) {
appointmentService.getAllDoctorAppointments(doctor, appointmentTypeArray)
.then(function (appointmentType) {
//could take in any number. hardcoded 3 just for testing.
console.log(appointmentType)
angular.forEach(appointmentType[0], function (xRay) {
$scope.xRayAppts.events.push(xRay);
});
angular.forEach(appointmentType[1], function (ctScan) {
$scope.ctScanAppts.events.push(ctScan);
});
angular.forEach(appointmentType[2], function (mri) {
$scope.mriAppts.events.push(mri);
});
});
};
this.getAllDoctorAppointments = function (doctor, appointmentTypeArray) {
var promises = [];
angular.forEach(appointmentTypeArray, function (appointmentType) {
promises.push($http.get('/appointments/?doctorName=' + doctor + '&apptType=' + appointmentType)
.success(function (listOfAppointments) {
return listOfAppointments;
});
);
});
return $q.all(promises);
};
$http.get returns the promises that you wants to collect, there is no need for a new defer in this case.
the promise is not being added to the array because the code that adds it to the array, promises.push(defer.promise);, is inside of the result code of the thing you are trying to defer. So the promise wouldn't get added to the list of promises to execute until after it executed!
so you can either move that push line outside of the success call looking something like this:
angular.forEach(appointmentTypeArray, function (appointmentType) {
var defer = $q.defer();
$http.get('/appointments/?doctorName=' + doctor + '&apptType=' + appointmentType)
.success(function (listOfAppointments) {
defer.resolve(listOfAppointments);
});
promises.push(defer.promise);
});
or, you can do as lcycool suggests and just add the $http.get(...).success(...) calls to the array directly.

AngularJS how do I execute code only after a promise is resolved? (with Restangular)

This might be a nooby question but I still haven't been able to get my head around promises and specifically how to write code with them. (I've read several articles but most of them are abstract and I simply haven't written enough to have a clear picture)
I've got an AngujlarJS application that gets data through a http request to another server which sends a promise at first. I've been able to retrieve the response from the promise and use it in my app. However because my code is poorly written. It executes other code before the promise is resolved leading to problems. It starts loading the page before it has the data.
what i have is:
var userTotals = *http request which returns a promise
$scope.data = userTotals.$object
//code that does someting with $scope.data
What i need is (I think)
var userTotals = *http request which returns a promise
$scope.data = userTotals.$object.
beforethisresolves(function{
show fancy loading icon or something })
.whenthis resolves(function{
//code that does someting with $scope.data
}
however I can't get the syntax correct.
This is what it looks like in general:
var promise = $http.post('/url');
console.log('Request started');
promise.then(function(result) {
console.log('Success');
console.log(result);
}, function() {
console.log('Failure');
});
In fact, $q AngularJS documentation helped me a good deal to get my head around promises concept.
Hope this helps!
var successCallback = function(){//...};
var errorCallback = function(){//...};
$http
.post('url', data)
.success(successCallback)
.error(errorCallback);
//OR
$http
.post('url', data)
.then(successCallback, errorCallback);
Assuming that you're using Bootstrap modal you can do the following:
function openModalWithProgressIndicator(deferred) {
const progressModal = $uibModal.open({
templateUrl: 'templates/modals/progress.html',
backdrop: 'static'
});
return deferred.then(function (value) {
return value;
}).finally(function () {
progressModal.close();
});
}
The deferred argument passed to this function is a promise. That said you can now do the following:
const deferred = $http.post('http://somewhere/data', {foo: 'bar'});
openModalWithProgressIndicator(deferred)
.then(function (httpResponse) {
// do sth with httpResponse.data
}).catch(function (error) {
// do sth with error
});
So the main point to note is the finally callback that's always executed.

q.all not working for multiple promises

I have the following q.all calling to resolve two promises. I checked all the posts and tried all other ways of implementation q.all and its the same case
var xyzdeffered = $q.defer();
service1.getServiceDetail1($routeParams.id).then(function(promise) {
xyzdeffered.resolve(promise);
});
var abcdeffered = $q.defer();
service2.getServiceDetail2($routeParams.id).then(function(promise) {
abcdeffered.resolve(promise);
});
$q.all([ xyzdeffered, abcdeffered ]).then(function(data) {
$scope.variable = data;
});
I am expecting the variable in q.all should get populated only after the first two promises are resolved. But unfortunately the service call itself doesn't get returned with data and the control moves on to the q.all. I find this weird because as per documentation the q.all is called only when your promises are returned with 200 response and are resolved.
I checked analysing the network calls and also put some alert to see the sequence of the code and find that the q.all alert is the first alert to be popped up and then the other promises are resolved.
Its really making me mad as why a simple implementation of q.all doesnt work..
Any help will be appreciated.
Why not directly call $q.all on first promise ?
$q.all([
service1.getServiceDetail1($routeParams.id),
service2.getServiceDetail2($routeParams.id)
]).then(function(data) {
//Array of result [resultOfgetServiceDetails1, resultOfgetServiceDetails2]
$scope.variable = data;
});
You need to reference the promise on the $q.defer() return object:
$q.all([ xyzdeffered.promise, abcdeffered.promise ])
For example you have you run multiple sq-lite queries synchronously just pass the array of queries(you can pass anything) as args into the call of this method.
function methodThatChainsPromises(args,tx) {
var deferred = $q.defer();
var chain = args.map(function(arg) {
var innerDeferred = $q.defer();
tx.executeSql(arg,[], function(){
console.log("Success Query");
innerDeferred.resolve(true);
}, function(){
console.log("Error Query");
innerDeferred.reject();
});
return innerDeferred.promise;
});
$q.all(chain).then(
function(results) {
deferred.resolve(true)
console.log("deffered resollve"+JSON.stringify(results));
}, function(errors) {
deferred.reject(errors);
console.log("deffered rejected");
});
return deferred.promise;
}

AngularJS promise and prevent redundant async calls

I have a wcf service method that gets some data and I call it using Microsoft Ajax library.
To share this data I create a dataService, and many controllers use this service.
I want every controller to get the same data after first call of getData is done, unless somebody need to refresh data and set forceRefresh to true.
My code fails because with the initialize of application 3 controler call dataService.getData and for all there start a new request. How can I make wait dataService.getData calls until the first one is finished and get same result for other subsequent ones..
angular.module('app', []).factory('dataService', function ($q) {
var data= [];
var getData= function (forceRefresh) {
console.log('getFolders called: ', reports.length);
var deferred = $q.defer();
if (forceRefresh || data.length < 1) {
WcfService.GetData(function(result) {
data= result;
deferred.resolve(data);
}, function(ex) { console.log(ex); });
} else {
deferred.resolve(reports);
}
return deferred.promise;
};
return {
getData: getData
};
});
One way would be to cache the promise, rather than the data, so it gets cached when it is created and not when the data arrives. After all, this sounds like your use case.
angular.module('app', []).factory('dataService', function ($q) {
var deferred = null;
var getData= function (forceRefresh) {
console.log('getFolders called: ', reports.length);
if(!forceRefresh && deferred) return deferred.promise;
deferred = $q.defer();
WcfService.GetData(
function(result) { deferred.resolve(data); }, // I'd promisify at a
function(ex){ deferred.reject(ex); } // higher level probably
);
return deferred.promise;
};
return {
getData: getData
};
});
how about setting a global flag in the $rootscope when a controller is querying for data, which can be checked before fetching the data by all the controllers, hence, avoiding redundant calls. The same flag can be put down when any of the controller has the promise fulfilled and data has been fetched, which can then be shared amongst all the controllers.
I found exactly what I search for
Promise queue for AngularJS services
http://inspectorit.com/tips-tricks/promise-queue-for-angularjs-services/

Resources