AngularJS $q - Return resolved directly [duplicate] - javascript

This question already has an answer here:
How to return fully resolved promise?
(1 answer)
Closed 7 years ago.
I use a method that expects a promise. Sometimes however, the promise is resolved instantly. Currently, if the promise is resolved right away, I still pass the promise, and use the following:
function instantAction()
{
var defer = $q.defer();
// Actions that are performed instantly
defer.resolve();
return defer.promise;
}
If there a way, to just return the resolve directly, such as something along the lines of return $q.resolve() and skip those two extra lines?

As #Blackhole and #ExpertSystem said above,
$q.when(<Data or 3rd-party Promise>) will return a promise.
It's intended to make an Angular promise out of data or a 3rd party promise.
See the docs here

Related

Why do we prefer using $q in angular instead of $http [duplicate]

This question already has answers here:
Why are AngularJS $http success/error methods deprecated? Removed from v1.6?
(2 answers)
Is this a "Deferred Antipattern"?
(3 answers)
Closed 4 years ago.
I am currently using $q service from angular to make API calls like this:
var deferred = $q.defer();
$http.get(config.apiHost + details.url)
.success(function (data) {
deferred.resolve(data);
}).error(function (msg) {
deferred.reject(msg);
});
return deferred.promise;
but we can also use this approach also without using $q:
return $http.get(config.apiHost + details.url)
.success(function (data) {
return data;
}).error(function (msg) {
return msg;
});
and as $http itself returns the promise, I can also use more simplified approach:
$http.get(config.apiHost + 'posts')
.success(function (data) {
console.log(data)
}).error(function (msg) {
console.log(msg);
});
So what is the difference between all these specially between $q and $http, as both returns promise and both are async ? Does angular provides some additional functionality with $q ?
I am not able to find any good answer.
$http uses $q, the first example is redundant, and so is the second. You just need to return the promise that $http.get returns:
return $http.get(config.apiHost + details.url);
The above is the same as your first piece of code.
In addition, return msg is not the same as deferred.reject(msg). The equivalent would be throw msg or return $q.reject(msg)
Another thing to note is that success and error are non-standard, you want to use then and catch.
$q is mainly only used for compatibility with libraries that don't support promises by default and when you can't rely on a native implementation of Promise (for example - in older browsers like IE9). There's no reason (for you) to use it otherwise. An example would if you wanted to make a promise-based $timeout. $http itself uses $q under the hood for these exact reasons.
Unlike what other (since deleted) answers have suggested, you do not need to use $q in order to "store" the result of the $http promise. I would not recommend storing the promise at all (as this tends to lead to spaghetti code), but if you must absolutely do this, you can just store the resultant promise from $http; promises only ever execute once.
Any functions passed to then after a promise has resolved/rejected will be resolved on the next tick, without re-invoking the original action that created the promise in the first place - IOW, the result of the promise is memoized within that object.
Also note that promises chain, which is abit out of scope for this answer, but it essentially means that the following pieces of code are equivalent
function legacyGet() {
const deferred = $q.defer()
$http.get('http://www.google.com')
.then((response) => deferred.resolve(Object.assign(response, {foo: bar}))
.catch((error) => deferred.reject(error))
return deferred.defer
}
function modernGet() {
return $http.get('http://www.google.com')
.then((response) => Object.assign(response, {foo: bar}))
}
To summarise: Your title is wrong. We don't prefer using $q, we only use it sparingly. Prefer to use an ES6 Promise unless you need to support browsers that don't have it and you can't use a polyfill.
In angular mostly all the services returns promises only, but there are some instances where you would like to create your own deferred object using $q.
Case 1
When you are using a library which does not support promise or you have created your own function and want to return a promise.
Case 2
When you are using any construct which by default returns a promise but you want to return a separate promise based on some on some condition.
Example: In angular $http returns a promise only but now if you want that if the response of this promise contains a particular value then only you want to return resolved else return failure then you need to create your own deffered object and need to resolve or fail it based on the value returned by $http response.

How to return data from promise [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
The community reviewed whether to reopen this question 11 months ago and left it closed:
Original close reason(s) were not resolved
I need to get the response.data out of the promise so it can be returned by the enclosing function. I know, I probably can't do it the way I've coded it because of normal JavaScript scope. Is there any way, it can be done?
The console.log at #1 produces the correct data. console.log #2 always produces 'a';
function addSiteParentId(nodeId) {
var theParentId = 'a';
var parentId = relationsManagerResource.GetParentId(nodeId)
.then(function(response){
theParentId = response.data;
console.log(theParentId); // #1
});
console.log(theParentId); // #2
return theParentId;
}
Any pointers would be appreciated.
One of the fundamental principles behind a promise is that it's handled asynchronously. This means that you cannot create a promise and then immediately use its result synchronously in your code (e.g. it's not possible to return the result of a promise from within the function that initiated the promise).
What you likely want to do instead is to return the entire promise itself. Then whatever function needs its result can call .then() on the promise, and the result will be there when the promise has been resolved.
Here is a resource from HTML5Rocks that goes over the lifecycle of a promise, and how its output is resolved asynchronously:
https://web.dev/promises/
I also don't like using a function to handle a property which has been resolved again and again in every controller and service. Seem I'm not alone :D
Don't tried to get result with a promise as a variable, of course no way. But I found and use a solution below to access to the result as a property.
Firstly, write result to a property of your service:
app.factory('your_factory',function(){
var theParentIdResult = null;
var factoryReturn = {
theParentId: theParentIdResult,
addSiteParentId : addSiteParentId
};
return factoryReturn;
function addSiteParentId(nodeId) {
var theParentId = 'a';
var parentId = relationsManagerResource.GetParentId(nodeId)
.then(function(response){
factoryReturn.theParentIdResult = response.data;
console.log(theParentId); // #1
});
}
})
Now, we just need to ensure that method addSiteParentId always be resolved before we accessed to property theParentId. We can achieve this by using some ways.
Use resolve in router method:
resolve: {
parentId: function (your_factory) {
your_factory.addSiteParentId();
}
}
then in controller and other services used in your router, just call your_factory.theParentId to get your property.
Referce here for more information:
http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx
Use run method of app to resolve your service.
app.run(function (your_factory) { your_factory.addSiteParentId(); })
Inject it in the first controller or services of the controller. In the controller we can call all required init services. Then all remain controllers as children of main controller can be accessed to this property normally as you want.
Chose your ways depend on your context depend on scope of your variable and reading frequency of your variable.
You have to return a promise instead of a variable.
So in your function just return:
return relationsManagerResource.GetParentId(nodeId)
And later resolve the returned promise.
Or you can make another deferred and resolve theParentId with it.

Angular $q.reject not resolving [duplicate]

This question already has answers here:
AngularJS : $q -> deferred API order of things (lifecycle) AND who invokes digest?
(2 answers)
Closed 6 years ago.
I have a very simple function in a jasmine test where I am simply returning $q.reject
var mockFunction = function() {
return $q.reject("error");
};
which I call:
mockFunction().catch(function(){
// ... blah
});
the catch callback function is never being invoked. If I understand correctly $q.resolve should create a promise, rejected immediately and return the rejected promise so I'm not sure why this is not working
Any insights are appreciated. Thanks!
$q promise chains aren't executed automatically in specs, it should be
mockFunction().catch(function(){
// ... blah
});
$rootScope.$digest();
to launch it, the chain will be executed synchronously in this case.

Get successful defers and ignore failed ones while processing array of defers [duplicate]

This question already has answers here:
jQuery.when - Callback for when ALL Deferreds are no longer 'unresolved' (either resolved or rejected)?
(8 answers)
Closed 7 years ago.
In my code below, if any defer fails it goes to fail block and everything is lost. What I want here is , to ignore failed ones and grab successful ones. Any graceful way to do it? and How would I go to failed block if all defers fail.
One work around could be to take a for loop and process deferredItems array in that loop one by one, but that is not graceful way to do it.
$.when.apply($, deferredItems).done(function(){
}).fail(function(errorObj) {
});
You can use .always, and then find the ones which were resolved, as you suggested
$.when.apply($, deferredItems).always(function(){
var resolvedDeferreds = $.grep(deferredItems, function(deferred){
return deferred.state() === 'resolved';
});
//do stuff with resolved deferreds
})
Edit:
It turns out this isn't going to work. The .always callback gets called immediately once one deferred fails and doesn't wait for the remaining pending deferreds. It looks like there's no way to wait for an array of defereds to all get either resolved or rejected using just jquery.when as is.
Use .always()
$.map([a, b], function(d) {
$.when(d).always(function(data) {
console.log(data)
})
})

Check if object is a promise in AngularJS [duplicate]

This question already has answers here:
Any way to know if a variable is an angularjs promise?
(4 answers)
Closed 8 years ago.
There are cases when I need to check if an object is a promise or not, e.g. to show a loading indicator when getting initial data from an API which might take a couple of seconds.
So far I'm using this code snippet:
if ($scope.data.$resolved === false){
// data is a promise as it is not resolved yet
}
else{
// data is not a promise as it either never was or has been resolved
}
This seems to work well but since I'm not an Angular expert (yet) I was wondering if there's a better / recommended way to check if an object is a promise. Thanks!
You don't need to know, if the promise is already resolved. If you pass your callback into the .then, this code will be executed, even if the promise is already resolved.
And it seems like a bad practice make your method return 2 different types.
A promise or the data directly.
Just always return a promise, even if the data is fetched from the cache.
Pass it into the .resolve, where ever the data did come from.
Something like this:
function getData(){
var data = getFromCache();
if( data ) {
var deferred = $q.defer(); //Make your own promise
deferred.resolve(data);
return deferred.promise;
}else{
return $http.get("someData.json") //Return the promise from the `$http.get`
.then(function(data){
return data;
});
}
}

Categories

Resources