Handle promise resolves indvidually in promise.all() - javascript

There is a lot of information on how to handle errors when using promise.all() using catch but what I'm trying to achieve is to handle every time a promise inside of this promise.all() resolves. The reason I'm trying to do this is because I am attempting to setup a custom progress bar in console and I need to call the tick method every time a promise is resolved.
this.getNewSources = function () {
var bar = new ProgressBar(':bar', {total: this.getSourceMap().size});
var timer = setInterval(function () {
bar.tick();
if (bar.complete) {
console.log('\ncomplete\n');
clearInterval(timer);
}
}, 100);
let promiseArr = [];
for (let x of this.getSourceMap().values()) {
promiseArr.push(this.requestArticles(x.getName(), x.getCat(), x.getKey()));
}
return Promise.all(promiseArr).then(() => {
console.log("Articles loaded this round: " + this.articles.size);
console.log('all sources updated');
this.loadedArticles = true;
console.log(this.articleCount);
console.log(this.articles.size);
}).catch(e => {
console.log(e);
});
};
I'm trying to figure out a way of being able to call the bar.tick() method when each individual promise resolves.

(Answering my own question.)
I handled it by adding a then handler where I'm getting the promise from requestArticles (where I'm pushing them into the promiseArr array). I had to be sure to pass the value that handler receives out of the handler so it propagates to Promise.all, see *** lines:
this.getNewSources = function () {
var bar = new ProgressBar(':bar', {total: this.getSourceMap().size});
var timer = setInterval(function () {
if (bar.complete) {
console.log('\ncomplete\n');
clearInterval(timer);
}
}, 100);
function updateProgressBar() {
bar.tick()
}
let promiseArr = [];
for (let x of this.getSourceMap().values()) {
promiseArr.push(this.requestArticles(x.getName(), x.getCat(), x.getKey())
.then(value => { // ***
updateProgressBar(); // ***
return value; // ***
}) // ***
);
}
return Promise.all(promiseArr).then(() => {
console.log("Articles loaded this round: " + this.articles.size);
console.log('all sources updated');
this.loadedArticles = true;
console.log(this.articleCount);
console.log(this.articles.size);
}).catch(e => {
console.log(e);
});
};
That way, my handlers get called as the promises complete individually, and since I'm returning the value I receive, the promise created by my call to then will resolve with that value, which Promise.all will see. Rejections will skip over that handler and go straight to the handlers hooked up by Promise.all.
The ascii progress library on npm
Result output in console:
(With thanks to T.J. Crowder for his initial explanation, which made me realize I could do this where I'm pushing onto the array. He said he preferred deleting that answer and having me post this instead.)

Related

Closure not updating inside a while loop in Javascript

I'm trying to get a closure to return a value that is supposed to be updated once a promise is resolved (or rejected).
The following code works. Initially the internal variable from within the close returns NONE as expected.
Then the first Promise is launched, and once that is resolved, the internal variable is updated to FAIL.
The second Promise is a deliberate delay, just so that we can observe the change of the closured variable.
However, once the while loop is added to the equation, by uncommenting that loop(x) section, the update is not observable within the while loop.
I would expect to see this:
...
9963000000 NONE
9964000000 NONE
9965000000 NONE
9966000000 NONE
9967000000 NONE
9968000000 FAIL
9969000000 FAIL
9970000000 FAIL
9971000000 FAIL
9972000000 FAIL
9973000000 FAIL
9974000000 FAIL
...
I know it might be due to the single threaded blocking, but, is there a way to observe a dynamic external variable from within the while loop?
let sleep = async (ms) => new Promise ((resolve, reject) => setTimeout (resolve, ms));
let task = async (ms) => new Promise (function(resolve, reject) {
setTimeout (function(){
const error = true;
let result;
if(error){
result = '_NO_';
reject({'state': false, 'response': result});
}else{
result = '_YES_';
resolve({'state': true, 'response': result});
}
}, ms);
});
let loop = async (cb) => {
let i = 0;
while(i<10000000000){
const value = cb.getResponse();
(function() {
if(i%1000000==0){ console.log(i, value) };
i += 1;
})(i, value);
}
}
const linkResponse = (function(){
let response = 'NONE';
function setResponse(value) {response = value; return response};
function getResponse() {return response};
return { 'setResponse': setResponse, 'getResponse': getResponse };
});
const x = linkResponse();
console.log(x.getResponse());
(async () => {
task(3000)
.then(function(res){
console.log('__OK__', res);
let response = 'SUCCESS';
x.setResponse(response)
})
.catch(function(err){
console.log('error', err);
let response = 'FAIL';
x.setResponse(response)
});
sleep(6000)
.then(function(res){
console.log(x.getResponse())
});
//loop(x);
})();
Well, thanks for the help. Just as I was suspecting, it is indeed a blocked thread issue. I solved the problem with a recursive function. I just needed to have a long process running in the background and I naively thought that an infinite loop will do the job.
let loop2 = function(i, cb) {
if(i>100000){
return
}
console.log(i, cb.getResponse());
i += 1;
sleep(0)
.then(function(res){
loop2(i, cb);
});
}
And then calling:
loop2(0, x);

Promise 2 resolves before the Promise 1

I am doing a search in the textfield and as I type, there is a call going to the backend after say 100ms.
For example, if we search "5041" and immediately search for "50" and again make it "5041", then there are 3 calls made to the backend.
1."5041" -> Promise 1
2."50" -> Promise 2
3."5041" -> Promise 3
However, promise 3 (web call takes 200ms) resolves before promise 2 (web call takes 500ms) which makes the screen reflect results for promise 2 ("50") when all I have in the textfield is "5041".
I need some way to let user type in the textfield without blocking the user along with the ability to show results for only the last call.
This is something that can be achieved using switchMap from rxjs in an angular app. However I need a way to achieve the same in vanilla JS.
First you can wrap your fetchData function into a something like fetchLatestSearchResults function which notes the time when network call was made and return the latest result from all the network calls(irrespective of what data was returned from server)
const generateLatestSearchFetch = function(fetchFunc){
let mostRecentResult = null;
let mostRecentResultFetchTime = Date.now();
return (...args) => {
const myFetchStartTime = Date.now();
return fetchFunc(...args)
.then(data => {
if (myFetchStartTime > mostRecentResultFetchTime) {
mostRecentResult = data;
mostRecentResultFetchTime = myFetchStartTime
}
return mostRecentResult;
});
}
};
Use Like:
fetchData = generateLatestSearchFetch(fetchData);
fetchData('10'); // resolves first and returns result for 10
fetchData('102'); // resolves third and returns result for 1024
fetchData('1024'); // resolves second and returns result for 1024
Last but not the least, use debounce more on this to optimize number of network calls made for every type event.
You need a "last" function:
// takes a function returning a promise and only reports the last resort
function last(fn) {
let p;
return function(...args) {
let current = fn(); // call the function
p = current; // mark it as the last call
return p.then(result => {
// ask am I still the last call?
if (p === current) return result;
else return new Promise(() => {}); // never resolve
});
}
}
let onlyLastSearch = last((name) => fetch('/api?name=' + name));
onlyLastSearch('a'); // will be ignored
onlyLastSearch('b'); // will be ignored
onlyLastSearch('c'); // only relevant result
You can use observer pattern for this.
const createWrapper = (fn) => {
let counter = 0;
let lastFetchId = 0;
const listeners = [];
return {
fetch: (str) => {
let id = ++counter;
fn(str).then((data) => {
if(id > lastFetchId) {
listeners.forEach(fn => {
fn(data);
});
lastFetchId = id;
}
});
},
listen: (fn) => {
listeners.push(fn);
return () => {
const index = listeners.indexOf(fn);
listeners.splice(index, 1);
};
}
}
}
const SearchWrapper = createWrapper(fetchData);
SearchWrapper.fetch('a');
SearchWrapper.fetch('b');
SearchWrapper.fetch('c');
SearchWrapper.listen((data) => {
console.log(data);
})

How to wait for few HTTP promises to complete and show a modal only if all the promises fail

I have two HTTP calls on a page and they are separate altogether.
vm.$onInit = function() {
....
....
//Get all the items only once during initialization
ds.getAllItems().then(function(result){
vm.items = result;
},function(error){
vm.errorInApi = true;
});
.....
.....
}
vm.getTimes = function(){
.....
.....
HttpWrapper.send(url,{"operation":'GET'}).then(function(times){
.....
}
If both the APIs fail then only I need to show a modal.
I can initiate a variable to true and on failure of the APIs, I can make that false and then only show the modal.
But then how long to wait for completion of all the APIs?
Hmm... simply invert the polarity of the promises and use Promise.all().
Promise.all() would normally resolve once all promises resolve, so once the promises are inverted, it resolves once all promises get rejected...
var invert = p => new Promise((v, x) => p.then(x, v));
Promise.all([Promise.reject("Error 404"), Promise.reject("Error WTF")].map(invert))
.then(v => console.log(v));
So as per #guest271314 comment i extend the solution in a silver spoon to show how inverting promises can be applied for this task.
var invert = p => new Promise((v, x) => p.then(x, v)),
prps = [Promise.reject("Error 404"), Promise.reject("Error WTF")]; // previously rejected promises
myButton.addEventListener('click', function(e){
setTimeout(function(...p){
p.push(Promise.reject("Error in Click Event Listener"));
Promise.all(p.map(invert))
.then(r => results.textContent = r.reduce((r,nr) => r + " - " + nr));
}, 200, ...prps);
});
<button id="myButton">Check</button>
<p id="results"></p>
If any of the promises including the previously obtained ones or the once in the event handler gets resolved you will get no output.
You can use async/await and .catch() to determine the number of rejected Promises, perform action if the number is equal to N, where N is the number of rejected Promise values required to perform action.
A prerequisite, as mentioned by #trincot, is to return the Promise from the function and return a value from the function passed to .then(), and throw an Error() from .catch() or function at second parameter of .then() see Why is value undefined at .then() chained to Promise?
const N = 2;
function onInit() {
return Promise.resolve("resolved")
}
function getTimes() {
return Promise.reject("rejected");
}
const first = onInit();
document.querySelector("button")
.onclick = async function() {
let n = 0;
const firstP = await first.catch(() => ++n);
const secondP = await getTimes().catch(() => ++n);
if (n === N) {
// do stuff if `n` is equal to `N`
} else {
// do other stuff
console.log(n, N)
}
};
<button>click</button>
You could use Promise.all.
First you should get the two promises in an array. To achieve that, it is probably useful if your two functions return the promise they create:
vm.$onInit = function() {
....
....
return ds.getAllItems().then(function(result){
vm.items = result;
},function(error){
vm.errorInApi = true;
});
}
vm.getTimes = function(){
.....
.....
return HttpWrapper.send(url,{"operation":'GET'}).then(function(times){
.....
});
}
The array would then be built from the return values:
var arr = [];
arr.push(vm.$onInit());
...
arr.push(vm.getTimes());
You write in a comment that getTimes "is called on some button click", so you would do that second push there.
Or, maybe you see another way to get these promises in an array... it does not matter much how you do it, as long as you achieve this.
Then (in that click handler) you need to detect the situation where both the promises are rejected. Promise.all can do that, but you need to flip the promise results:
// Flip promises so that a rejected one is regarded as fulfilled and vice versa:
arr = arr.map(p => p.then(res => { throw res }).catch(err => err));
// Detect that all original promises rejected, i.e. that the new promises all fulfill.
Promise.all(arr).then(function() {
// execute whatever you need to execute when both ajax calls failed
}).catch(err => err); // ignore

JS Promise - instantly retrieve some data from a function that returns a Promise

Can anyone recommend a pattern for instantly retrieving data from a function that returns a Promise?
My (simplified) example is an AJAX preloader:
loadPage("index.html").then(displayPage);
If this is downloading a large page, I want to be able to check what's happening and perhaps cancel the process with an XHR abort() at a later stage.
My loadPage function used to (before Promises) return an id that let me do this later:
var loadPageId = loadPage("index.html",displayPage);
...
doSomething(loadPageId);
cancelLoadPage(loadPageId);
In my new Promise based version, I'd imagine that cancelLoadPage() would reject() the original loadPage() Promise.
I've considered a few options all of which I don't like. Is there a generally accepted method to achieve this?
Okay, let's address your bounty note first.
[Hopefully I'll be able to grant the points to someone who says more than "Don't use promises"... ]
Sorry, but the answer here is: "Don't use promises". ES6 Promises have three possible states (to you as a user): Pending, Resolved and Rejected (names may be slightly off).
There is no way for you to see "inside" of a promise to see what has been done and what hasn't - at least not with native ES6 promises. There was some limited work (in other frameworks) done on promise notifications, but those did not make it into the ES6 specification, so it would be unwise of you to use this even if you found an implementation for it.
A promise is meant to represent an asynchronous operation at some point in the future; standalone, it isn't fit for this purpose. What you want is probably more akin to an event publisher - and even that is asynchronous, not synchronous.
There is no safe way for you to synchronously get some value out of an asynchronous call, especially not in JavaScript. One of the main reasons for this is that a good API will, if it can be asynchronous, will always be asynchronous.
Consider the following example:
const promiseValue = Promise.resolve(5)
promiseValue.then((value) => console.log(value))
console.log('test')
Now, let's assume that this promise (because we know the value ahead of time) is resolved synchronously. What do you expect to see? You'd expect to see:
> 5
> test
However, what actually happens is this:
> test
> 5
This is because even though Promise.resolve() is a synchronous call that resolves an already-resolved Promise, then() will always be asynchronous; this is one of the guarantees of the specification and it is a very good guarantee because it makes code a lot easier to reason about - just imagine what would happen if you tried to mix synchronous and asynchronous promises.
This applies to all asynchronous calls, by the way: any action in JavaScript that could potentially be asynchronous will be asynchronous. As a result, there is no way for you do any kind of synchronous introspection in any API that JavaScript provides.
That's not to say you couldn't make some kind of wrapper around a request object, like this:
function makeRequest(url) {
const requestObject = new XMLHttpRequest()
const result = {
}
result.done = new Promise((resolve, reject) => {
requestObject.onreadystatechange = function() {
..
}
})
requestObject.open(url)
requestObject.send()
return requestObject
}
But this gets very messy, very quickly, and you still need to use some kind of asynchronous callback for this to work. This all falls down when you try and use Fetch. Also note that Promise cancellation is not currently a part of the spec. See here for more info on that particular bit.
TL:DR: synchronous introspection is not possible on any asynchronous operation in JavaScript and a Promise is not the way to go if you were to even attempt it. There is no way for you to synchronously display information about a request that is on-going, for example. In other languages, attempting to do this would require either blocking or a race condition.
Well. If using angular you can make use of the timeout parameter used by the $http service if you need to cancel and ongoing HTTP request.
Example in typescript:
interface ReturnObject {
cancelPromise: ng.IPromise;
httpPromise: ng.IHttpPromise;
}
#Service("moduleName", "aService")
class AService() {
constructor(private $http: ng.IHttpService
private $q: ng.IQService) { ; }
doSomethingAsynch(): ReturnObject {
var cancelPromise = this.$q.defer();
var httpPromise = this.$http.get("/blah", { timeout: cancelPromise.promise });
return { cancelPromise: cancelPromise, httpPromise: httpPromise };
}
}
#Controller("moduleName", "aController")
class AController {
constructor(aService: AService) {
var o = aService.doSomethingAsynch();
var timeout = setTimeout(() => {
o.cancelPromise.resolve();
}, 30 * 1000);
o.httpPromise.then((response) => {
clearTimeout(timeout);
// do code
}, (errorResponse) => {
// do code
});
}
}
Since this approach already returns an object with two promises the stretch to include any synchronous operation return data in that object is not far.
If you can describe what type of data you would want to return synchronously from such a method it would help to identify a pattern. Why can it not be another method that is called prior to or during your asynchronous operation?
You can kinda do this, but AFAIK it will require hacky workarounds. Note that exporting the resolve and reject methods is generally considered a promise anti-pattern (i.e. sign you shouldn't be using promises). See the bottom for something using setTimeout that may give you what you want without workarounds.
let xhrRequest = (path, data, method, success, fail) => {
const xhr = new XMLHttpRequest();
// could alternately be structured as polymorphic fns, YMMV
switch (method) {
case 'GET':
xhr.open('GET', path);
xhr.onload = () => {
if (xhr.status < 400 && xhr.status >= 200) {
success(xhr.responseText);
return null;
} else {
fail(new Error(`Server responded with a status of ${xhr.status}`));
return null;
}
};
xhr.onerror = () => {
fail(networkError);
return null;
}
xhr.send();
return null;
}
return xhr;
case 'POST':
// etc.
return xhr;
// and so on...
};
// can work with any function that can take success and fail callbacks
class CancellablePromise {
constructor (fn, ...params) {
this.promise = new Promise((res, rej) => {
this.resolve = res;
this.reject = rej;
fn(...params, this.resolve, this.reject);
return null;
});
}
};
let p = new CancellablePromise(xhrRequest, 'index.html', null, 'GET');
p.promise.then(loadPage).catch(handleError);
// times out after 2 seconds
setTimeout(() => { p.reject(new Error('timeout')) }, 2000);
// for an alternative version that simply tells the user when things
// are taking longer than expected, NOTE this can be done with vanilla
// promises:
let timeoutHandle = setTimeout(() => {
// don't use alert for real, but you get the idea
alert('Sorry its taking so long to load the page.');
}, 2000);
p.promise.then(() => clearTimeout(timeoutHandle));
Promises are beautiful. I don't think there is any reason that you can not handle this with promises. There are three ways that i can think of.
The simplest way to handle this is within the executer. If you would like to cancel the promise (like for instance because of timeout) you just define a timeout flag in the executer and turn it on with a setTimeout(_ => timeout = true, 5000) instruction and resolve or reject only if timeout is false. ie (!timeout && resolve(res) or !timeout && reject(err)) This way your promise indefinitely remains unresolved in case of a timeout and your onfulfillment and onreject functions at the then stage never gets called.
The second is very similar to the first but instead of keeping a flag you just invoke reject at the timeout with proper error description. And handle the rest at the then or catch stage.
However if you would like to carry the id of your asych operation to the sync world then you can also do it as follows;
In this case you have to promisify the async function yourself. Lets take an example. We have an async function to return the double of a number. This is the function
function doubleAsync(data,cb){
setTimeout(_ => cb(false, data*2),1000);
}
We would like to use promises. So normally we need a promisifier function which will take our async function and return another function which when run, takes our data and returns a promise. Right..? So here is the promisifier function;
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
Lets se how they work together;
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function doubleAsync(data,cb){
setTimeout(_ => cb(false, data*2),1000);
}
var doubleWithPromise = promisify(doubleAsync);
doubleWithPromise(100).then(v => console.log("The asynchronously obtained result is: " + v));
So now you see our doubleWithPromise(data) function returns a promise and we chain a then stage to it and access the returned value.
But what you need is not only a promise but also the id of your asynch function. This is very simple. Your promisified function should return an object with two properties; a promise and an id. Lets see...
This time our async function will return a result randomly in 0-5 secs. We will obtain it's result.id synchronously along with the result.promise and use this id to cancel the promise if it fails to resolve within 2.5 secs. Any figure on console log Resolves in 2501 msecs or above will result nothing to happen and the promise is practically canceled.
function promisify(fun){
return function(data){
var result = {id:null, promise:null}; // template return object
result.promise = new Promise((resolve,reject) => result.id = fun(data, (err,res) => err ? reject(err) : resolve(res)));
return result;
};
}
function doubleAsync(data,cb){
var dur = ~~(Math.random()*5000); // return the double of the data within 0-5 seconds.
console.log("Resolve in " + dur + " msecs");
return setTimeout(_ => cb(false, data*2),dur);
}
var doubleWithPromise = promisify(doubleAsync),
promiseDataSet = doubleWithPromise(100);
setTimeout(_ => clearTimeout(promiseDataSet.id),2500); // give 2.5 seconds to the promise to resolve or cancel it.
promiseDataSet.promise
.then(v => console.log("The asynchronously obtained result is: " + v));
You can use fetch(), Response.body.getReader(), where when .read() is called returns a ReadableStream having a cancel method, which returns a Promise upon cancelling read of the stream.
// 58977 bytes of text, 59175 total bytes
var url = "https://gist.githubusercontent.com/anonymous/"
+ "2250b78a2ddc80a4de817bbf414b1704/raw/"
+ "4dc10dacc26045f5c48f6d74440213584202f2d2/lorem.txt";
var n = 10000;
var clicked = false;
var button = document.querySelector("button");
button.addEventListener("click", () => {clicked = true});
fetch(url)
.then(response => response.body.getReader())
.then(reader => {
var len = 0;
reader.read().then(function processData(result) {
if (result.done) {
// do stuff when `reader` is `closed`
return reader.closed.then(function() {
return "stream complete"
});
};
if (!clicked) {
len += result.value.byteLength;
}
// cancel stream if `button` clicked or
// to bytes processed is greater than 10000
if (clicked || len > n) {
return reader.cancel().then(function() {
return "read aborted at " + len + " bytes"
})
}
console.log("len:", len, "result value:", result.value);
return reader.read().then(processData)
})
.then(function(msg) {
alert(msg)
})
.catch(function(err) {
console.log("err", err)
})
});
<button>click to abort stream</button>
The method I am currently using is as follows:
var optionalReturnsObject = {};
functionThatReturnsPromise(dataToSend, optionalReturnsObject ).then(doStuffOnAsyncComplete);
console.log("Some instant data has been returned here:", optionalReturnsObject );
For me, the advantage of this is that another member of my team can use this in a simple way:
functionThatReturnsPromise(data).then(...);
And not need to worry about the returns object. An advanced user can see from the definitions what is going on.

Why does my Promise Chain not work in this nested way?

I want to chain 4 functions in a Promise chain like so:
function1 -> function2 -> function3 -> function4
My Promise chain
if ($location.$$url !== "/dashboard") {
vm.customURL = true;
// (1) Set root vars & Rebuild tickerTagsContainer:
var promise = TagFactory.buildUrlObject($location.$$url).then(function() {
console.log('TagFactory.buildUrlObject PROMISE returned');
}).then(function() {
console.log('(2) Re-display tags in viewHeader');
// (2) Re-display tags in viewHeader:
viewHeader = ScopeFactory.getScope('viewHeader');
viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
});
}).then(function() {
// (3) Reselect timeSpan:
console.log('(3) Reselect timeSpan');
viewHeader.vh.toggleTimeSpan(vm.timeSpan);
// (4) Refresh URL:
console.log('(4) Refresh URL');
ViewFactory.remakeViewObject($location.$$url);
});
}
The resulting console.logs:
^ Note I never see this log:
viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
});
Ideally I want to place my (3) function inside that, then chain my (4) like so:
viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
console.log('(3) Reselect timeSpan');
viewHeader.vh.toggleTimeSpan(vm.timeSpan).then(function() {
console.log('(4) Refresh URL');
ViewFactory.remakeViewObject($location.$$url);
});
});
However I never see the console.log from the .then function for displayViewHeaderTags
Here is what my displayViewHeaderTags looks like:
function displayViewHeaderTags() {
vm.viewTickerTags = [];
vm.viewTickerTags = TagFactory.retrieveTickerTags('all');
var deferred = $q.defer();
var tikObjs = vm.viewTickerTags.map(function(el) { return el.ticker; });
var tagObjs = vm.viewTickerTags.map(function(el) { return el.tags; });
var tags = _.flatten(tagObjs);
// forEach loops up to 3 times:
tags.forEach(function(tag, i) {
vm.viewTags = [];
ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
vm.viewTags.push(data.data.ticker_tag);
if (i === tags.length) {
deferred.resolve();
}
});
});
return deferred.promise;
}
Inside my displayViewHeaderTags function I hit a loop which will run up to 3 times, after it's done getting data, it will fill up and array then calls deffered.resolve. then returns it return deferred.promise;
So why do I never see this log? console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
Your i is never the same as the length, because the i variable starts at zero (array indexes start at zero). Which means the if you have an array with length = 2, your i values will be 0 and 1 respectively. It will never equal to zero. Basically, you would want the condition to be:
vm.viewTags.push(data.data.ticker_tag);
if (i + 1 === tags.length) {
deferred.resolve();
}
Anyway, using defer() is a code smell.
A more elegant way of doing it would be using $q.all
var allPromises = [];
var promise;
tags.forEach(function(tag) {
vm.viewTags = [];
promise = ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
vm.viewTags.push(data.data.ticker_tag);
});
// Create an array of promises, one promise for each request
allPromises.push( promise );
});
// Return a new promise that will only be resolved
// when all the promises of the array `allPromises` are resolved,
// or is rejected when one of them is.
return $q.all( allPromises );
Your chain is not really doing anything since you're not returning a promise from any of those anonymous functions. You're not seeing that log probably because ApiFactory.getTagDataSilm is failing or never resolving. Try adding an error handler into your flow.
if ($location.$$url !== "/dashboard") {
vm.customURL = true;
// (1) Set root vars & Rebuild tickerTagsContainer:
var promise = TagFactory.buildUrlObject($location.$$url).then(function() {
console.log('TagFactory.buildUrlObject PROMISE returned');
}).then(function() {
console.log('(2) Re-display tags in viewHeader');
// (2) Re-display tags in viewHeader:
viewHeader = ScopeFactory.getScope('viewHeader');
return viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
});
}).then(function() {
// (3) Reselect timeSpan:
console.log('(3) Reselect timeSpan');
return viewHeader.vh.toggleTimeSpan(vm.timeSpan);
}).then(function() {
// (4) Refresh URL:
console.log('(4) Refresh URL');
return ViewFactory.remakeViewObject($location.$$url);
}).catch(function(error) {
console.log('Something failed', error);
});
}
Within displayViewHeaderTags, you can use $q.all, so that rejections are handled for you:
// forEach loops up to 3 times:
vm.viewTags = [];
return $q.all(_.map(tags, function(tag) {
return ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
vm.viewTags.push(data.data.ticker_tag);
});
}));

Categories

Resources