I am creating a deferred and then returning a promise in a function that involves async behavior.
var deferred = $q.defer();
//...make use of the deferred
return deferred.promise;
I want to add a shortcut to the top of the function that will bypass the async activity and effectively return a resolved promise immediately. How should I do this?
e.g. would this be idiomatic?
if (shouldShortcut) {
return $q.when(true);
}
You can just resolve the deferred immediately, but still return its promise:
if (shouldShortcut) {
deferred.resolve();
return deferred.promise;
}
Edit: I see now that you are talking about $q and not Q. See below the break for my previous answer as it relates to the Q library.
Based on Benjamin Gruenbaum's comment below (which he has since deleted), $q.when() is a fine way to do this:
var resolvedPromise = $q.when();
You can resolve with a particular value by passing that into when():
var resolvedPromise = $q.when("all good");
There's no need to involve deferreds here. In fact, I would suggest limiting your use of deferreds, as they will most likely soon be passé in favor of the revealing constructor pattern that is used in ES6.
(previous answer)
The Q library provides a method for doing this that is consistent with the ES6 promises standard:
Q.Promise.resolve();
this produces a resolved promise.
If you want to resolve it with a particular value, you can pass in that value:
Q.Promise.resolve("all good"); // promise resolved with the value "all good"
Related
this post extends a previous one already solved. Please see it, to get in context: Nesting promises with $resources in AngularJS 1.0.7
With that approach in mind, I would like to make a parallel call to functions and when both are completed then run the searchBoats function with the results. I think I can nest the promises, but I also think it can be done in parallel, maybe with $q.all. Unfortunately, this promises topic is confusing for me and I don´t know how to do it.
Taking the previous post example, I would like to do something like:
var parseURL = function() {
var deferred = $q.defer();
var promise = deferred.promise;
promise.then(function success(result) {
console.log(result);
searchBoats(result);
});
// Instead of resolve when parseBoatType promise is returned, I would like to
// wait for two promises
parseBoatType().then(deferred.resolve);
parseDestination().then(deferred.resolve);
// of course, with this code deferred will be resolved when first promise
// comes. Is it possible to use $q.all here?
};
parseURL();
As in your earlier question, there is no need to use a deferred here. Just use $q.all(), which returns a promise:
var parseURL = function() {
$q.all([parseBoatType(), parseDestination()])
.then(function (results) {
console.log(results);
searchBoats(results);
});
};
parseURL();
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.
find is a function that returns a promise that resolves to a value, but also has data on the promise object itself. A practical use case is when find() returns a dbQueryObject that can be used elsewhere, whereas await find() resolves with databaseResults. eg:
function find () {
var results = new Promise(/* resolves with databaseResults */)
Object.assign(results, new dbQueryClass)
return results
}
I can wrap find with a function to provide helpers like this,
function findPage (page) {
return find().skip(page*20).limit(20)
}
and use it like findPage().select('_id') to get the query object or await findPage().select('_id') to get the resolved value (returns something similar to find()).
However, if I wrap find with a promise like this,
async function findSomething() {
var someArgs = await promise1()
if (someArgs) return find(someArgs)
return find()
}
How do I get the value of find() itself outside of findSomething? I need promise1 to resolve, but I need find() to not resolve. find() is a plain object AND a thenable, depending on if you resolve it or not, but resolving findSomething() will automatically resolve the returned value. How do I return it such that it doesn't resolve?
My constraint is that I cannot modify the library that provides find() to not return a thenable object.
I don't see what would "usually" be an antipattern here. You can return or await any promise, thenable or plain value in an async function, just as you can Promise.resolve them or return them from a then callback. There is nothing unusual about this, that's just how it is done.
If you want to be explicit about returning a promise, instead of relying on thenable assimilation, you can always call .exec() on your mongoose query and it will get you a "real" promise.
As stated here https://stackoverflow.com/a/22724984/704894, it's impossible to have Promise<Promise<value>> because when you return a Promise inside .then handler, it gets automatically resolved (unwrapped).
You'd have to use Promise<Wrapper<Promise>> to bypass this limitation.
Sidenote for functional programming folks: .then is both map and flatMap in Haskell terms.
I have a question about attaching callback functions to promises in AngularJS.
Suppose I have a service with a function that returns a promise. I make a call to this function and store the promise locally. Then I define a callback function on the promise.
var promise = TestService.get();
console.log('We have a promise!');
promise.then(function (result){
console.log('Here is the result:'+result);
});
In this case, we have a potentially risky situation. If the promise is resolved before we get to promise.then(..., the result is not outputted to the console (until the next digest cycle).
Alternatively, I could write the above code like this:
TestService.get().then(function (result){
console.log('Here is the result:'+result);
});
My question:
Has the risk been mitigated in the second example? And if not, how can I make sure that the promise does not resolve before I have attached a callback?
A slightly more elaborate answer than yes/no would be much appreciated :)
The behavior you are describing does not occur, that can be seen through a simple example. Here we have a simple promise factory which returns a promise which resolves immediately.
'use strict';
var make = function() {
return new Promise(function(resolve, reject) {
resolve(2);
});
};
Then we create a new promise and assign it to a variable
var prom = make();
We can call .then on it as many times as we want. This is because promises are immutable, we don't change the original value by chaining methods on it.
prom.then(a => console.log(a));
// 2
prom.then(a => console.log(a));
// 2
Suppose I have a service with a function that returns a promise. I make a call to this function and store the promise locally. Then I define a callback function on the promise.
No, you are not attaching a callback. When you call the then method you are doing something called promise chaining. Each call to then returns a new promise object that will resolve to the value returned by the previous promise.
For example;
var promise1 = TestService.get();
var promise2 = promise1.then(function(value) {
console.log('service resolved: '+value);
return "Hello World";
});
var promise3 = promise2.then(function(value) {
console.log(value);
});
promise3.then(function(value) {
console.log(value);
});
The above example will output the following.
**some value from TestService**
Hello World
undefined
We don't know who originally resolve the value for the first promise. All we know is that the service returned a promise. From that moment on we can chain the promises by adding more calls to then.
In this case, we have a potentially risky situation. If the promise is resolved before we get to promise.then(..., the result is not outputted to the console (until the next digest cycle).
No, it does not matter when or what digest the promise is resolved. A promise can have it's then method called multiple times even after being resolved. It will continue to resolve to the value as long as it has not been rejected. The decision to resolve or reject a promise is outside the scope of the success or failure callbacks.
You can create a promise, resolve it to a value, wait several digests and add a handler to then and it will still work as expected.
Has the risk been mitigated in the second example? And if not, how can I make sure that the promise does not resolve before I have attached a callback?
Think of promises as containers. They are going to hold the value you expect, and you have to call then to get it. If for what ever reason the value is unavailable you can find out why by using the error callback. The when aspect of promises is purely an asynchronize issue, and the idea is for promises to hide those issues.
JavaScript is not multithreaded, your asynchronous AJAX call isn't actually made by the browser until your code returns.
var promise = TestService.get();
for (var i= 0;i<100000;i++){
console.log(i)
}
console.log('We have a promise!');
promise.then(function (result){
console.log('Here is the result:'+result);
});
Watch this with the network analyzer.
I'm trying to make sense of the idea that promises that return another promise become the value of the return. That's what I read in the Q docs on the wiki. https://github.com/kriskowal/q#tutorial
Its the ideal situation, as I'm using q-io/http which uses a promise to make the request. However, the function returns the value of the body in a new promise.
Rather than nesting the rest of my code inside my first function. I want to be notified when the nested promise is complete. However, because of scope, I can't seem to do it. But I keep reading that my outer function should become the value of the inner promise. Am I missing something?
Ex.
module.exports = function (obj) {
var getFiles = HTTP.request(obj);
getFiles
.then(function(res) {
return res.body.read()
});
return getFiles;
};
I want to be notified when res.body.read() fulfills its promise using Q.all(), but I don't know how to get that value. I feel like getFiles should become that inner promise. Right?
I feel like getFiles should become that inner promise. Right?
No. A promise won't change it's value. However, the .then method does return a new promise that will be resolved with the result of the "inner promise" read method (which will be called when getFiles is resolved).
You can shorten your code to
module.exports = function (obj) {
return HTTP.request(obj).then(function(res) {
return res.body.read()
});
};