Can you resolve an angularjs promise before you return it?
AngularjsPromiseAngularjs Problem Overview
I am trying to write a function that returns a promise. But there are times when the information requested is available immediately. I want to wrap it in a promise so that the consumer doesn't need to make a decision.
function getSomething(id) {
if (Cache[id]) {
var deferred = $q.defer();
deferred.resolve(Cache[id]); // <-- Can I do this?
return deferred.promise;
} else {
return $http.get('/someUrl', {id:id});
}
}
And use it like this:
somethingService.getSomething(5).then(function(thing) {
alert(thing);
});
The problem is that the callback does not execute for the pre-resolved promise. Is this a legitimate thing to do? Is there a better way to handle this situation?
Angularjs Solutions
Solution 1 - Angularjs
Short answer: Yes, you can resolve an AngularJS promise before you return it, and it will behave as you'd expect.
From JB Nizet's Plunkr but refactored to work within the context of what was originally asked (i.e. a function call to service) and actually on site.
Inside the service...
function getSomething(id) {
// There will always be a promise so always declare it.
var deferred = $q.defer();
if (Cache[id]) {
// Resolve the deferred $q object before returning the promise
deferred.resolve(Cache[id]);
return deferred.promise;
}
// else- not in cache
$http.get('/someUrl', {id:id}).success(function(data){
// Store your data or what ever....
// Then resolve
deferred.resolve(data);
}).error(function(data, status, headers, config) {
deferred.reject("Error: request returned status " + status);
});
return deferred.promise;
}
Inside the controller....
somethingService.getSomething(5).then(
function(thing) { // On success
alert(thing);
},
function(message) { // On failure
alert(message);
}
);
I hope it helps someone. I didn't find the other answers very clear.
Solution 2 - Angularjs
How to simply return a pre-resolved promise in Angular 1.x
Resolved promise:
return $q.when( someValue ); // angular 1.2+
return $q.resolve( someValue ); // angular 1.4+, alias to `when` to match ES6
Rejected promise:
return $q.reject( someValue );
Solution 3 - Angularjs
Here's how I typically do it if I want to actually cache data in array or object
app.factory('DataService', function($q, $http) {
var cache = {};
var service= {
getData: function(id, callback) {
var deffered = $q.defer();
if (cache[id]) {
deffered.resolve(cache[id])
} else {
$http.get('data.json').then(function(res) {
cache[id] = res.data;
deffered.resolve(cache[id])
})
}
return deffered.promise.then(callback)
}
}
return service
})
Solution 4 - Angularjs
You forgot to initialize the Cache element
function getSomething(id) {
if (Cache[id]) {
var deferred = $q.defer();
deferred.resolve(Cache[id]); // <-- Can I do this?
return deferred.promise;
} else {
Cache[id] = $http.get('/someUrl', {id:id});
return Cache[id];
}
}
Solution 5 - Angularjs
I like to use a factory to get the data from my resource something like.
.factory("SweetFactory", [ "$http", "$q", "$resource", function( $http, $q, $resource ) {
return $resource("/sweet/app", {}, {
"put": {
method: "PUT",
isArray: false
},"get": {
method: "GET",
isArray: false
}
});
}]);
Then expose my model in the service like this here
.service("SweetService", [ "$q", "$filter", "$log", "SweetFactory",
function ($q, $filter, $log, SweetFactory) {
var service = this;
//Object that may be exposed by a controller if desired update using get and put methods provided
service.stuff={
//all kinds of stuff
};
service.listOfStuff = [
{value:"", text:"Please Select"},
{value:"stuff", text:"stuff"}];
service.getStuff = function () {
var deferred = $q.defer();
var promise = SweetFactory.get().$promise.then(
function (response) {
if (response.response.result.code !== "COOL_BABY") {
deferred.reject(response);
} else {
deferred.resolve(response);
console.log("stuff is got", service.alerts);
return deferred.promise;
}
}
).catch(
function (error) {
deferred.reject(error);
console.log("failed to get stuff");
}
);
promise.then(function(response){
//...do some stuff to sett your stuff maybe fancy it up
service.stuff.formattedStuff = $filter('stuffFormatter')(service.stuff);
});
return service.stuff;
};
service.putStuff = function () {
console.log("putting stuff eh", service.stuff);
//maybe do stuff to your stuff
AlertsFactory.put(service.stuff).$promise.then(function (response) {
console.log("yep yep", response.response.code);
service.getStuff();
}).catch(function (errorData) {
alert("Failed to update stuff" + errorData.response.code);
});
};
}]);
Then my controllers can include it and expose it or do what it fells is right in its context simply by referencing the injected Service.whatever
Seems to work ok. But I am kinda new to angular. *error handling mostly left out for clarity