AngularJS 1.0.7: Nesting parallel promises with $resources - javascript

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();

Related

Resolving promises synchronously with Q.all(promises)

I am using the Kriskowal Q library to handle promises.
I wrote the following function to wait until all promises have been resolved.
function multiplePromises(targetArray) {
var promises = {};
for (var i=0; i<targetArray.length; i++) {
var promise = singlePromise(); // any async. function
promises[i] = promise;
};
return Q.all(promises);
};
and I call it as follows:
multiplePromises(targetArray).then(
function(success){
console.log("success");
},
function(error){
console.log("error", error);
}
);
I was wondering however whether there is an order in which the promises are resolved (e.g. is it synchronous?). I.e. does the function wait to trigger the next promise i+1 until promise i is resolved? Or alternatively is it like with all other async. methods, that it actually fires all the single promises and just waits until they are all done?
If the second is the case, how would one rewrite this function to make sure that promise i+1 is ONLY triggered once promise i has been resolved?
Update: test
I did a test and put:
promises[i] = i;
to check whether it resolves sycnhronously and it seems the case. However, it could be just that my async function is fast enough to actually resolve it that quick. Does this seem right?
there are several ways to achieve what you want
Minimal change to your code would be
function multiplePromises(targetArray) {
var promises = [];
var p = Promise.resolve(); // I don't know the Q equivalent of this
for (var i=0; i<targetArray.length; i++) {
p = p.then(function() {
return singlePromise();
});
promises[i] = p;
};
return Q.all(promises);
};
The promises will be executed by Q in the order you declare them, but there's no way to ensure the return order will be the same. That is how async works.
If you want them to resolve in order, the only way I can think of is calling them one after the other instead of doing it in a batch.
This other response will provide more info and some solutions:
https://stackoverflow.com/a/36795097/7705625
Promises in theory could be resolved in order (and it could be easy to write an example in which they would) but you should never count on that.
The whole point of functions like Promise.all() (or Q.all() or Bluebird.all() etc.) is that if waits for all of the promises to get resolved, no matter what is the order of that, and as soon as they are all resolved then the promise that the Promise.all() itself returns gets resolved with an array of values that the original promises resolved to.
In that array you will get always the same order as the order of promises in the array that was an argument to Promise.all(). But the order in time in which the original promises were resolves is not known and is not relevant, as that will have no effect on the result of using Promise.all() whatsoever.

Short-cutting a promise

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"

How to run after all javascript ES6 Promises are resolved

I'm in the process of replacing some old code that used jQuery Deferred objects and I am rewriting using Bluebird/ES6 Promises.
If I have multiple asynchronous calls, how can I trigger a function after all the promises are resolved.
Using jQuery Deferreds it would be something like this:
var requests = [...]; //some arbitrary data that is iterated to generate multiple ajax requests
var promises = [];
resuests.forEach(function(endpoint) {
promises.push($.ajax({url: endpoint}));
});
$.when.apply($, promises).then(function() {
alert('all promises complete!');
});
How do I rewrite this using ES6 Promise syntax?
Using Promise.all. Note that it takes an iterable such as an Array as its argument, unlike $.when, so doesn't need the .apply.
You'll also want to convert the jQuery Deferred to a native ES6 promise using Promise.resolve(thejQueryDeferred). EDIT: This gets done implicitly by the call to Promise.all, so is really optional.
Whole code:
var requests = [...]; //some arbitrary data that is iterated to generate multiple ajax requests
var promises = [];
requests.forEach(function(endpoint) {
var nativePromise = Promise.resolve($.ajax({url: endpoint})); // if you want to make it clear that you're converting from jQuery Deferred to ES6 promise!
promises.push(nativePromise);
});
Promise.all(promises).then(function() {
alert('all promises complete!');
});
Since this is tagged bluebird in addition to the two good solutions you have already gotten here is a more "bluebird" way:
var requests = [...];
Promise.map(requests, $.get).then(function(results){
alert('all promises complete!');
});
This is probably as simple as it gets.
As the others have pointed out, the native es6 way would be to use Promise.all, no Promise.resolve or explicit creation is needed. The cleanest way with native promises would probably be:
var requests = [...];
Promise.all(requests.map($.get)).then(function(results){
});

Jasmine async test using promises

I am doing some Jasmine testing using angular promises and have a question related to timing. Found an answer here Unit-test promise-based code in Angular, but need clarification about how this works. Given that the then method is always handled in an asynchronous way how is the following test guaranteed to pass. Isn't there are risk that the expect will run ahead of the then block being executed and run the expect before the value has been assigned. Or... does the digest cycle guarantee that the value will be assigned before the expect runs. Meaning, a digest cycle will effectively behave like a blocking call that guarantees that the promises are all resolved before the code is allowed to proceed.
function someService(){
var deferred = $q.defer();
deferred.resolve(myObj);
return deferred.promise;
}
it ('testing promise', function() {
var res;
var res2;
someService().then(function(obj){
res = "test";
});
someService().then(function(obj){
res2 = "test2";
});
$rootScope.$apply();
expect(res).toBe('test');
expect(res2).toBe('test2');
});
a digest cycle will effectively behave like a blocking call that guarantees that the promises are all resolved before the code is allowed to proceed.
Yes, although more accurately it would guarantee that the success callbacks of resolved promises would have run.
There is a very similar example that shows how the digest cycle is tied to the success callbacks of promises in the docs for $q
While Michal's answer points to the right idea, the key here is that $apply() is called on the pertinent scope. Here's the example from the Angular docs:
it('should simulate promise', inject(function($q, $rootScope) {
var deferred = $q.defer();
var promise = deferred.promise;
var resolvedValue;
promise.then(function(value) { resolvedValue = value; });
expect(resolvedValue).toBeUndefined();
// Simulate resolving of promise
deferred.resolve(123);
// Note that the 'then' function does not get called synchronously.
// This is because we want the promise API to always be async, whether or not
// it got called synchronously or asynchronously.
expect(resolvedValue).toBeUndefined();
// Propagate promise resolution to 'then' functions using $apply().
$rootScope.$apply();
expect(resolvedValue).toEqual(123);
}));

Is there a way to trigger call a method in another module that "might" exist?

I know this is a base question... but I'll try to explain.
I've been using a Deferred, but someone pointed out that I'm using it as an anti-pattern.
Basically, I can use the deferred in the child module. BUT if this is an anti-pattern, what is the best way to achieve this in which the "child module" might not be there.. SO, I don't want to explicitly call the child method in the success of the parent ajax call.
So, three questions:
Is this a viable way, if not - what is a good way to achieve what I am going for?
Even though I am resetting the Deferred, it isn't seeming to 'reset'. Why?
CAN this also be achieved coupling it with another event - ie, onload. SO< we have the ajax AND onload doing the same thing.
I have a parent module.
var Z = {};
Z.someVar = $.Deferred();
Z.a = function(){
//methods
//Ajax call on page: can be once or multiple calls.
$AJAX = {
url:...,
dataType: ...,
//etc..
}.success(
function(){
Z.someVar.resolve();
}
).error();
};
// another method that MIGHT exist.
Z.b = function(){
var func1 = function(){
// do some stuff
Z.someVar = $.Deferred(); // reset it.
}
Z.someVar.done(function(){
func1();
})
}
A promise is an abstraction over a single, one time calculation of a value. A promise starts pending, and then can transition to either fulfilled or rejected state. Once a promise resolved (transitioned) it can never change its state again.
Deferreds are used in exactly one - when you want to construct a promise from something that's not a promise*.
In jQuery $.ajax already returns a promise to begin with.
Z.a = function(){ // this method now returns a promise
return $.ajax({...});
};
If we want to chain it, we can use .then :
Z.a().then(function(){
// here a() is done.
});
You can also use a caching pattern, using $.when (Promise.resolve in most promise libraries):
Z._a = null;
Z.a = function(){
if(z._a){ // already have the value cached:
return $.when(z._a); // promise over the value
}
return $.ajax({...}).then(function(res){
Z._a = res;
return res; // chain
});
};
Z.b = function(){
return Z.a().then(function(aValue){
// do something with value, your return value here is the resolution
// value of the a method call.
});
}
Now, if you want to wait for multiple calls, you can use $.when with multiple promises and get their results.
There are other, very rare use cases with aggregation

Categories

Resources