can't get $q.all work on multiple promises - javascript

I really struggle with that... I have a function in my controller generating a certain amount of promises, passed to another function which also generate promises...
angular.forEach($scope.items,function(value,key) {
myService.getPage(item).then(function(data) {
myFunction(data)
});
function myFunction(singlePage) {
myService.getPage(singlePage.next).then(function(data) {
myFunction(data)
});
}
What i want to achieve is to somehow know when all promises will be resolved
I tried to wrap my promises in an array and pass it to $q.all in all kind of configuration but without success..Any kind of help would be really cool !! Thanks

Ignoring the infinite recursion loop you've created, let's do this...
var allPromises = [];
angular.forEach($scope.items,function(value,key) {
allPromises.push(myService.getPage(value));
});
$q.all(allPromises)
.then(function (arrayOfResults) {
// do something
});
One thing to note, you are passing in value and key, but using "item". You need to be using value.

Related

How and when to rewrite jQuery callbacks into promises?

I consider rewriting existing callback-based code into code using promises. However I'm unsure if this makes sense and how to start. The following code-snippet is a mostly self-contained example from that code:
function addTooltip($element, serverEndpoint, data) {
'use strict';
const DELAY = 300;
const TOOLTIP_PARENT_CLASS = 'hasTooltip';
let timeOutReference;
$element.hover(function hoverStart() {
if ($element.hasClass(TOOLTIP_PARENT_CLASS)) {
return;
}
timeOutReference = setTimeout(function getToolTip() {
const $span = jQuery('<span class="serverToolTip">');
$span.html(jQuery('<span class="waiting">'));
$element.append($span);
$element.addClass(TOOLTIP_PARENT_CLASS);
jQuery.get(serverEndpoint, data).done(function injectTooltip(response) {
$span.html(response.data);
}).fail(handleFailedAjax);
}, DELAY);
}, function hoverEnd() {
clearTimeout(timeOutReference);
});
};
Intended functionality: When the user hovers over $element for 300ms the tooltip-content is requested from the server and appended to $element.
Does it make sense to rewrite that code with promises and how would I do it?
(jQuery is provided by the framework (dokuwiki), so we might as well use it.)
Prior research:
https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
other SO questions about this topic which left me unsure about whether this is a sensible idea and how to do it
First, you'd need to wrap setTimeout into a promise. Simply create a function that accepts a timeout and returns a promise that resolves after that timeout.
Next, since jQuery.get already returns a promise, you just need to put it inside the promise resolve handler and return its promise. That way the next chained then listens to that promise instead of the timer's.
It would look something like:
function timer(n){
return Promise(function(resolve){
setTimeout(resolve, n);
});
}
timer(DELAY).then(function(){
return jQuery.get(...)
}).then(function(response){
// jQuery.get promise resolved
}).catch(function(error){
// something failed somewhere
});
As for your question
Does it make sense to rewrite that code with promises and how would I do it?
That depends on you. I find promise-based code more readable but takes time to write properly especially if if you intend to write pure callbacks and deal with multiple async operations. I usually write my code callbacks-first if the API is simpler to write that way and refactor later for readability.
To elaborate on my comment above. Below is an example of how promises can make dependent-callback code (arguably) more readable (Basically, it destroys the nesting of callbacks-in-callbacks):
Again, in the case of the code snippet you posted, I hardly see how it's worth it (unless your doing it as an exercise).
With Callbacks
function someAsyncMethod(callback) {
$.get({...})
.then(callback);
}
function anotherAsyncMethod(callback) {
$.get({...})
.then(callback);
}
someAsyncMethod(function() {
anotherAsyncMethod(function yourFunction() {
//do something
});
});
With Promises:
function someAsyncMethod() {
return $.get({...});
}
function anotherAsycnMethod() {
return $.get({...});
}
someAsyncMethod()
.then(anotherAsyncMethod)
.then(function yourFunction() {
//do something
})

Promise not working in chain of javascript /jquery functions

I have a series of async functions that i want to execute in a sequence, i tryed using promises but the exmple i followed tho it works, does not execute then in orther, and many times one resolves the content, before the other draws the container. i have tryed to understand the promises thing and it seems that the promises is being rejected, but i dont know what im doing wrong, or if i should use other method to "chaing" the functions. Thanks for the help!
var chain = new Promise(function(){
statusUp();
});
chain.then(p_view(pid)).then(pu_list(pid)).then(pm_list(pid));
You're looking for something similar to this
Promise.resolve(statusUp())
.then(function(pid) {
return p_view(pid)
})
.then(function(pid) {
return pu_list(pid)
})
.then(function(pid) {
return pm_list(pid)
});
I made some (a lot of...) assumptions here regarding statusUp, p_view, pu_list and pm_list so this might need some tweaking.

Working with 1 or more jQuery promises

I'm making either 1 or more REST/ajax calls to validate some user information. The rest calls are working well and the information is coming back. The issue I'm facing isn't with that part of the code, which looks something like this.
function ensureUsers(recipients){
var promises = [];
for(var key in recipients){
var payload = {'property':recipients[key]};
promises.push( $.ajax({...}));
}
return $.when.apply($,promises);
}
....
ensureUsers(users) // users is an array of 1 or more users
.done(function(){
console.log(arguments);
)}
If there is more than one user in the initial array, then the arguments in my .done code are structured like this:
[[Object,"success",Object],[Object,"success",Object]...]
I can then iterate over each result, check the status, and proceed.
However if there is only one user in the initial array then .done gets arguments like this:
[Object,"success",Object]
It seems strange to me that the structure of what is returned would change like that. I couldn't find anything about this specific a problem, so I hacked together a solution
var promises = Array.prototype.slice.call(arguments);
if(!Array.isArray(promises[0])){
promises = [promises];
}
Is that really the best I can hope for? Or is there some better way to deal with the returned promises from 1 or more ajax calls in jQuery?
It seems strange to me that the structure of what is returned would change like that.
Yes, jQuery is horribly inconsistent here. When you pass a single argument to $.when, it tries to cast it to a promise, when you pass multiple ones it suddenly tries to wait for all of them and combine their results. Now throw in that jQuery promises can resolve with multiple values (arguments), and add a special case for that.
So there are two solutions I could recommend:
Drop $.when completely and just use Promise.all instead of it:
var promises = [];
for (var p of recipients) {
…
promises.push( $.ajax({…}));
}
Promise.all(promises)
.then(function(results) {
console.log(results);
})
Make each promise resolve with only a single value (unlike $.ajax() that resolves with 3) so that they don't get wrapped in an array, and $.when will produce consistent results regardless of number of arguments:
var promises = [];
for (var p of recipients) {
…
promises.push( $.ajax({…}).then(function(data, textStatus, jqXHR) {
return data;
}) );
}
$.when.apply($, promises)
.then(function() {
console.log(arguments);
})
It appears this functionality is working as currently documented. When you have multiple deferreds passed to $.when it creates a new Promise object that is resolved with the results of each of the results of the passed in deferreds. When you only pass in one, it returns the deferred that you passed in, thus only returning the result instead of an array of results.
I'm not sure if it is any better than your current solution, but you could force it to always have multiple deferreds by having a "fake" deferred that you skip when evaluating the results.
I.E.
function ensureUsers(recipients){
var promises = [$.Deferred().resolve()];
for(var key in recipients){
var payload = {'property':recipients[key]};
promises.push( $.ajax({...}));
}
return $.when.apply($,promises);
}
You could also potentially make it so the placeholder deferred is resolved with the same structure as what you expect in your real results so it would just appear that the first response is always a success.

AngularJs - Share data across multiple functions inside factory

I am just starting to learn Angularjs so i might be using the whole thing wrong but please correct me.
I have the following factory that goes like this:
app.factory('MenuService',['service1','service2','service3',function(service1,service2,service3){
var var1 = [],
var2 = [],
var3 = [];
service1.getDataMethod(function(data){
// processes data and saves it in var1
});
service2.getDataMethod2(function(data)){
});
/// same goes for service3.
return {"prop2": var1, "prop2" : var2, "prop3": var3};
}])
I need to process the data return by service2 , based on data returned in the first service but every time i try to access it, the variable is empty.
I know the functions return a promise, is there a way to tell the second function to wait for the first one to finish in order to be able to use the data it brings?
I hope i made myself understood. Let me know if i should add something else.
Like this?
service1.getDataMethod(function(data){
// processes data and saves it in var1
}).then(function(data) {
return service2.getDataMethod2(data)
})
Basically each promise have .then() method. So you can chain this as much as you need a.then().then().then()
In addition, some promise implementations have method wrappers such as .success() and .error(). These methods works similar to .then()
in angular you have access to $q service that is implementation of q library that works with promises. https://docs.angularjs.org/api/ng/service/$q
If you don't like it for some reason try async
Basically you have to chain your promises:
service1.getDataMethod(function(data){
return something; // this is important, only that way you will be
// able to use the next .then()
})
.then(function(something) { // what you returned previously will be the function's argument
return service2.getDataMethod2(data);
})
.then(function(result) { // `result` is what getDataMethod2 resolves
})
.catch(function(err) {
// very important to add .catch to handle errors
});

Outer variables not being altered inside Promise.then function

On node while using thinky.js, I am trying to iterate through a loop and add each item to an array. This however, for some reason is not working.
In another place, it is indentical and working, just without a Promise.then function. Why is this not working?
var fixedItems = [];
for (i in tradeItems) {
var item = tradeItems[i];
Item.get(item["id"]).run().then(function(result) {
var f = { "assetid": result["asset_id"] };
console.log(f); // WOrks
fixedItems.push(f); // Doesn't work
});
}
console.log(fixedItems); // Nothing
A Promise represents the future result of a task. In this case you're logging fixedItems before your tasks (the calls to Item.get) have finished working. In other words, the then functions haven't run yet so nothing has been put into fixedItems.
If you want use fixedItems once it contains all of the items, you'll need to wait for all of the promises to resolve.
How you do that depends on the Promise library you're using. This example, with Promise.all, works with many libraries including native ES6 Promises:
// Get an array of Promises, one per item to fetch.
// The Item.get calls start running in parallel immediately.
var promises = Object.keys(tradeItems).map(function(key) {
var tradeItem = tradeItems[key];
return Item.get(tradeItem.id);
});
// Wait for all of the promises to resolve. When they do,
// work on all of the resolved values together.
Promise.all(promises)
.then(function(results) {
// At this point all of your promises have resolved.
// results is an array of all of the resolved values.
// Create your fixed items and return to make them available
// to future Promises in this chain
return results.map(function(result) {
return { assetid: result.asset_id }
});
})
.then(function(fixedItems) {
// In this example, all we want to do is log them
console.log(fixedItems);
});
Recommended reading: the HTML5 rocks intro to Promises.
Your problem is that you are calling console.log(fixedItems) before any of the promises in the loop have finished executing. A better way of doing this that would also solve the asynchronous problem is to put all the item IDs in an array first and retrieve all the items in a single query, which is also more efficient on the database side.
var itemIds = tradeItems.map(function(item) {
return item.id;
});
var fixedItems = [];
//you would need to write your own getItemsById() function or put the code
//to get the items here
getItemsById(itemIds).then(function(items) {
items.forEach(function(item) {
var f = { "assetid": result["asset_id"] };
fixedItems.push(f);
});
whenDone();
});
function whenDone() {
//you should be able to access fixedItems here
}
I couldn't easily find how to look up multiple records by ID in a single query with thinky, but I did find this page which might help:
http://c2journal.com/2013/01/17/rethinkdb-filtering-for-multiple-ids/
While this would be my preferred way of solving this problem, it would also be possible to still use multiple queries and use a promise chain to wait for them all to be resolved before continuing to your subsequent code. If you wanted to go that route, check out this link: http://promise-nuggets.github.io/articles/11-doing-things-in-parallel.html. (Note: I haven't personally used Bluebird, but I think the Bluebird example in that link may be out of date. The map method appears to be the current recommended way to do this with promises: https://stackoverflow.com/a/28167340/560114.)
Update: Or for this latter option, you can just use the code in joews's answer above.

Categories

Resources