How to be explicit in resolving deferred promise in angular $q? - javascript

If I have something in my service like
myServiceMethod: function(){
$http.get(myUrl)
.success(function(result){$q.defer().resolve(result);})
.error(function(error){$q.defer().resolve(error);});
return $q.defer().promise;
}
and in my controller
myService.myServiceMethod()
.then(function(result){})
.then(function(error){});
is there a way to be explicit in the name space? Because it seems like the deferred promises can get messy if you start nesting deferred resolve. For example
myServiceMethod: function(){
$http.get(myUrl)
.success(
function(result){
if(result){
$q.defer().resolve(result);
}else{
$q.defer().resolve(myCustomresult);
}
})
.error(function(error){$q.defer().resolve(error);});
return $q.defer().promise;
}

You are creating too many deferred objects and the one being returned is not what you are resolving or rejecting
Just return the $http which itself returns a promise. what you are trying to do is considered an anti-pattern
myServiceMethod: function(){
// return the `$http` promise
return $http.get(myUrl)
.then(function(result){return result.data);})
// either catch it here or catch in controller
.catch(function(error){ alert('Error')});
}
Controller
myService.myServiceMethod()
.then(function(result){})
.catch(function(error){});

Every time you call $q.defer() you are creating a new promise, which isn't the right thing to do.
The $http.get method itself returns a promise, so unless you are doing something else that needs to run asynchronously, you do not need to use $q
For arguments sake, you can do this:
myServiceMethod: function() {
var myPromise = $q.defer();
$http.get(myUrl).success(function(result){
if(result)
myPromise.resolve(result);
else
myPromise.reject(result);
});
return myPromise.promise;
}

Could be much shorter:
Service
myServiceMethod: function () {
return $http.get(myUrl).then(function (response) {
return response.data || myCustomResult; // default result if API returned nothing
});
}
Controller
myService.myServiceMethod()
.then(function (result) { /* do something ... */ })
.catch(function (error) { /* handle error ... */ });

Related

AngularJS chain promises sequentially within for loop

How do i chain promises sequentially within for loop, i have seen lot of examples on google to do this but i couldn't implement for my case:
i have gone through this link for sequential chaining of Promises.
What I'm trying to acheive:
Promise1: login();
Promise2: sync();
sync function calls another service complete() for an array of elements. These array of elements must be done sequentially.
ServiceA.login().
then(function(response){
ServiceA.sync()
.then(function(response){
})
})
function sync(){
ServiceB.complete()
.then(function(){
var promises = [];
angular.forEach(response, function (value) {
// The below service call doSomething() must be done sequentially for each "value"
promises.push(doSomething(value));
});
$q.all(promises).then(function () {
});
});
})
}
How do I capture the error occuring in each Promise?
Update:
I have tried the approach suggested by #zaptree with the following code:
ServiceA.login()
.then(function(response){
// you must always return your promise
return ServiceA.sync()
})
// don't nest the .then make them flat like this
.then(function(response){
})
.catch(function(){
// if you made sure to always return your promises this catch will catch any errors throws in your promise chain including errors thrown by doSomething()
});
function sync(){
// you must always return your promise
return ServiceB.complete()
.then(function(){
var result = $q.when();
angular.forEach(response, function (value) {
result = result.then(doSomething(value)); // problem is here that doSomething function is being called before the first call it is resolved
// doSomething is a http call.
});
return result;
})
.then(function(){
// the array of promises has run sequentially and is completed
});
}
function doSomething(data){
return $http({
method: 'POST',
url: '/api/do',
data: data,
headers: {
"Content-Type": "application/json"
}
}).then(function (response) {
}, function (error) {
});
}
If the response in the near the for each loop has 2 values (valuea, valueb) in it, the code is behaving as follows:
1. calling doSomething(valuea)
2. calling doSomething(valueb) before the above promise is resolved.
Expected behaviour:
after the POST method has succesfully completed by the call doSOmething(valuea), then the another POST call should happend i.e., soSomething(valueb).
Here's what I came up with. You'll need to reduce the array into a single promise.
var results = [...];
var sequentialPromise = results.reduce(function(a, b) {
return a.then(function(){
return doSomething(b);
});
}, $q.resolve());
sequentialPromise.then(function(){...});
So here is an example on how you would do the sequential promises with Q, also some improvements on how to do your promises so you can properly catch errors thrown at any point in your promise chain. You must always make sure to return a promise on any method that uses them. Also avoid pyramid code by not nesting the .then to make your code cleaner:
ServiceA.login()
.then(function(response){
// you must always return your promise
return ServiceA.sync()
})
// don't nest the .then make them flat like this
.then(function(response){
})
.catch(function(){
// if you made sure to always return your promises this catch will catch any errors throws in your promise chain including errors thrown by doSomething()
});
function sync(){
// you must always return your promise
return ServiceB.complete()
.then(function(){
var result = $q.when();
angular.forEach(response, function (value) {
result = result.then(doSomething(value));
});
return result;
})
.then(function(){
// the array of promises has run sequentially and is completed
});
}

Chain multiple promises in Angularjs

I have the following structure and need to simplify it,
Resuable service1 to interact with IndexedDB
function isDbExist (){
// do something , it return either reject or resolve
}
function createDB (){
// do something , it return either reject or resolve
}
function getData (){
// do something , it return either reject or resolve
}
In another service2 I'm injecting this service1 and using the functions as such
function1 (){
service1.isDbExist.then(function(data){
service1.createDB.then(function(data){
service1.getData.then(function(data){
referred.resolve(data);
},function(error){
deferred.reject(error)
})
},function(error){
deferred.reject(error);
})
},function(error){
deferred.reject(error);
})
}
The problem here is the readability of the code is not good, its not easy to debug as which reject function is for which promise. Is their some good way of doing this ? I have read about $q.all but don't this its applicable in this situation.
Exactly, the beauty of promises is that you can chain them as opposed to nesting them like callbacks. You can return another promise when dealing with the previous like so:
isDbExists().then(function(db) {
// do something
return createDb();
}).then(function(db) {
// do something with with the resolved db..
return getData();
}).then(function(data) {
// Do something with data
}).catch(function(error) {
// Uh oh, something went wrong. Deal with error
});
Finally, you deal with the error that may have occurred.
You can chain multiple promises like this:
service1.isDbExist()
.then(service1.createDB)
.then(service1.getData)
.then(function() {
// DB exists, created and data loaded
})
.catch(function(err) {
console.log('Handle error', err); // isDbExist, createDB or getData rejected
});
Check interactive demo to see how success and error data is passed to the next promise in chain and how you have full control over process on each step:
Demo: http://plnkr.co/edit/PM06e8NGJvHKmoJ9C2Lf?p=info
One of the really great things about promises is that they can be chained like this:
function1 (){
return service1.isDbExist.then(function(exists){
if(!exists) return service1.createDB()
})
.then(function() {
return service1.getData('some', 'args')
});
}
Remember that the .then will receive the result of the previous function, like the exists would be a boolean. function1 will also return a promise and this promise will return the resolved/rejected promise from service1.getData.

Rejecting Angular $http promise

I need to reject an $http promise call in the success/then function. I can see 2 options: 1) thrown an error - this garbages up the console and 2) use separate defer call and reject that. Is there a cleaner/more concise way? I have a feeling I am overlooking something obvious.
Error:
return $http.get(url}.then(function(r) {
throw new Error('Booh');
});
With $q:
var deferred = $q.defer();
$http.get(url}.then(function(r) {
deferred.reject("Booh");
});
return deferred.promise;
Try this:
function myfunction()
{
return $http.post('url')
.then(function(response){
// check success-property of returned data
if(response.data === 'Something you dont want')
return $q.reject('some error occured');
else{
return $q.resolve(response.data);
}
})
}
You can use de reject method of the $q service to immetiately forward to the next error section, something like:
var deferred = $q.defer();
$http.get(url}.success(function(r) {
deferred.reject();
}).error(function(){
// do something
});
When success q.reject will forward to the error and do something...
As $http is a modified promise do something like :
var deferred = $q.defer();
$http.get(url).success(function(r) {
if(r != 'your criteria')
deferred.reject("Booh");
else
deferred.resolve("Baah");
}).error(function(){
deferred.reject("Beeh");
});
return deferred.promise

Parse.Promise and angluarjs $q promise

I am trying to use Parse sdk with angularjs and I am a bit confused on handling promises when using implementation of promises in parse and angular.js
Parse promise provides a method fail to catch error where as angularjs provides us with catch. So say if I am inside Parse promise object and have to return say angularjs promise from it or vice versa. It won't have I guess fail method in it
ParsePromise().then(function(){
return angularPromise();
}).fail(function (error){
});
AngularPromise().then(function(){
return ParsePromise();
}).catch(function (error){
});
What should I do in such scenario?
I will suggest to use $q. Just wrap parse promises in angular's $q promises. then use only angularjs promise handling.
findByIdsInParse = function(arr) {
var ParseObject = ParseService.Object.extend('ParseObject');
var deffered = $q.defer();
var query = new ParseService.Query(ParseObject);
query.containedIn('objectId', arr);
query.find({
success: function(data) {
deffered.resolve(data);
},
error: function(error) {
deffered.reject(error);
}
});
return deffered.promise;
};
We can wrap a parse function into a return $q((resolve,reject)=>{}) then we can use them in all $q functions. So something like this:
findTransactionById(id){
return $q((resolve,reject) => {
//... set up variables, queries ...
query.find().then(resolve,reject)
})
}
//we can then use them in all $q functions
findTransactionById(12).
then(console.log)
let promises = [findTransactionById(10),findTransactionById(9)]
$q.all(promises)
.then((values) => {
return findTransactionById(999)
})
.catch(console.error)

Async.js return promise

I'm using the async.js library to achieve an stream of asynchronous requests.
The code below works fine.
async.each(
//--- Collection of models to save ---//
collSaveData,
function(model, callback){
model.save([], {success: function(){
callback();
}});
},
function(err){
console.log('finished');
});
How can I return a promise?
I mean, something in a fashion like this:
var promise = async.each(
//--- Collection of models to save ---//
collSaveData,
function(model, callback){
model.save([], {success: function(){
callback();
}});
},
function(err){
console.log('finished');
});
You probably don't need async.js to issue you calls and synchronize them. Combine the objects returned by Model.save with $.when to produce a general promise :
var promises = _.invoke(collSaveData, 'save');
var promise = $.when.apply(null, promises);
promise.then(function() {
console.log('all done');
});
And a Fiddle http://jsfiddle.net/nikoshr/Z3Ezw/
You can customize how you handle the responses from each save, for example:
var promises = _.map(collSaveData, function(m) {
return m.save().then(function(response) {
console.log('saved', m);
});
});
The key is to return a promise for each model. http://jsfiddle.net/nikoshr/Z3Ezw/2/
Not actually used async before but looking through the docs it makes no reference to returning a promise so you would have to wrap the asyn.each with a function that did return a promise and then the success/error callback could then just resolve or reject that promise
here is a quick example that should work
//wrap the async each and return a promise from this call
var promiseAsync = function(openfiles, saveFile) {
var defer = $.Deferred();
async.each(
openfiles, saveFile, function(err) {
if (err) {
defer.reject(err);
} else {
defer.resolve();
}
});
return defer.promise();
}
//now it can be used like a normal promise
var promise = promiseAsync(collSaveData, function(model, callback) {
model.save([], {
success: function() {
callback();
}
});
});
$.when(promise).done(function(){
//whatever
}).fail(function(err){
//error
});
make promiseAsync available throughout your app (attach it to Backbone somewhere, underscore mixin,helper/utility module??) and then you can always use it.
Update: fiddle based on #nikoshr's fiddle (for setting up the page) (going to start using fiddles over code pen now like the fact you can have a console in the browser) http://jsfiddle.net/leighking2/7xf7v/

Categories