Leon Gaban
Leon Gaban

Reputation: 39018

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: enter image description here

^ 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!');

Upvotes: 0

Views: 438

Answers (2)

Pedro M. Silva
Pedro M. Silva

Reputation: 1298

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

Upvotes: 2

Anid Monsur
Anid Monsur

Reputation: 4538

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

Upvotes: 1

Related Questions