I have a chained promise and in the case of a rejection for either of the promises, I need to perform an async operation (get the translated error message). As I've already got a chained promise on success, I assume it's not possible to also chain on rejection - I am attempting to simply nest the async calls, but I'm not getting the resolved promise back from deferred.reject(deferredRejection.promise);
below. Pointers appreciated!
login: function(email, password) {
var deferred = $q.defer();
AuthService.login(email, password).then(function(response) {
var user = {
'accountToken': response.accountToken,
'email': response.username,
'onboarded': response.onboarded,
'verified': response.verified
};
return SyncStorageService.write(SyncStorageService.storageKeys.user,
user);
}, function(error) {
// login failed
var deferredRejection = $q.defer();
$translate('ALERTS.LOGIN_FAILED').then(function(translatedValue) {
deferredRejection.resolve(translatedValue);
});
deferred.reject(deferredRejection.promise);
}).then(function(data) {
deferred.resolve(data);
}, function(error) {
// saving data failed
var deferredRejection = $q.defer();
$translate('ALERTS.UNKNOWN').then(function(translatedValue) {
deferredRejection.resolve(translatedValue);
});
deferred.reject(deferredRejection.promise);
});
return deferred.promise;
}
Updated Solution:
Based on the answer below, I was able to re-write the code as follows:
login: function(email, password) {
return AuthService.login(email, password).then(function(response) {
return {
'accountToken': response.accountToken,
'email': response.username,
'onboarded': response.onboarded,
'verified': response.verified
};
}).then(function(data) {
return SyncStorageService.write(SyncStorageService.storageKeys.user,
data);
});
}
Notes:
- Both
AuthService.login
andSyncStorageService.write
now reject promises with anError
object (e.g.new Error('ALERT.ERROR_MESSAGE');
), which bubbles up throughlogin
to the controller (previously was doing the translation at the service level); - The controller that calls the
login
method has.then()
and.catch()
blocks - on a .catch(), the passedError.message
is translated and displayed.
It looks like you're not really chaining promises, and using the forgotten promise/deferred anti pattern. Making a few assumptions about how you actually want it to behave, and factoring out the calls to
$translate
, then something like the following I suspect is what you're after:The main things to keep in mind are:
If you definitely want to reject the derived promise, return
$q.reject(error)
from the success or error callback.All the error callbacks above do this. The ones after attempted login or saving use the translation key as the error that will be eventually passed to the final
catch
callback. The success callback from$translate
also does this to transform its resolved promise to a rejected one, so the final catch callback returns a rejected promise, so the calling code gets a rejected promise, and (presumably) shows the translated error to the user.If you definitely want to resolve the derived promise, return anything that isn't a promise from he success or error callbacks. The derived promise will be resolved with that value. (This includes
undefined
if you don't have an explicit return value).This is what is done above when returning the user
return {'accountToken'....
in the first callback.If you want to defer the resolution/rejection of a promise, return another promise from the success or error callback. Then the derived promise will be eventually resolved or rejected in whatever manner this other promise is resolved/rejected.
This is what's done above when returning
SyncStorageService.write...
, and when returning$translate(...
.