1

I am working my way through promises and noticed the following. I am listening for changes in my controller like such:

$scope.$on("New Data", function(event, data, ID) {
    processTripData(data, ID).then(function(result) {
        setTypeDistributionBar(result).then(function(data, labels) {
            $scope.distributionLabels = labels;
            $scope.distributionData = data;
            console.log(labels); // undefined, why?
            console.log(data); // defined, as expected
        })
    })
})

The issue I have is that labels is undefined, however in the setTypeDistributionBar function it is defined before it gets resolved:

var setTypeDistributionBar = function(trips) {
    var distributionData = [];
    var distributionLabels = [];
    var types = ['Transport', 'Office & Work'];

    return new Promise(function(resolve, reject) {
        for(var i=0; i < trips.length; i++) {
            var first = trips[i].type.slice(0,1);
            var second = trips[i].type.slice(2,3);
            distributionLabels.push(types[first-1] + ' -> ' + types[second-1]);
            distributionData.push(trips[i].count);
        }
        if(i === trips.length) {
            console.log(distributionLabels); // defined, as expected
            resolve(distributionData, distributionLabels);
        }
    })
}

Question: Why is labels undefined in the then part in the first function, but it is defined when logging it just before resolving in the second function?

laurent
  • 88,262
  • 77
  • 290
  • 428
ffritz
  • 2,180
  • 1
  • 28
  • 64
  • It's `Promise.resolve(value);` not `Promise.resolve(value1[, value2[, ...[, valueN]]]);` – Andreas Jun 12 '17 at 11:29
  • By the way, why do you check for `if(i === trips.length)`? Wouldn't that always be true? And, if not, shouldn't you call `reject()` in the `else` part? (otherwise the caller will get stuck waiting). – laurent Jun 12 '17 at 11:37
  • @this.lau_ Well I am just starting out with promises and thought that once `i` reached the length, the for loop is done and thus the promise can be resolved. I did not find any other understandable examples on how to write a promise for a for loop. – ffritz Jun 12 '17 at 11:38
  • Based on your code, I think you can remove this check and simply call `resolve(...)` just after the loop. What's inside the promise function will be run sequentially so if this part of the code is executed you can be sure that the loop before it has been executed too. – laurent Jun 12 '17 at 11:40
  • @this.lau_ Okay interesting. Is this always the case? Because I am struggling a lot with finding examples on how to wait properly for a for loop to finish. Sometimes stuff after the loop gets executed before the loop is done. – ffritz Jun 12 '17 at 11:41
  • 1
    If there are async calls within the loop (not the case of what you've posted) then these calls might finish after the loop is complete. In that case, you'll need to chain the async calls and wait for all of them to finish. This is a possible solution: https://stackoverflow.com/a/44053362/561309 but it might depend on your specific case. – laurent Jun 12 '17 at 11:46

1 Answers1

1

A promise encapsulates one value and one value only. It's easier to consider that a promise is basically the value that a function would return (a function also can only return one value).

So this is not valid:

setTypeDistributionBar(result).then(function(data, labels) {

since function will only receive one argument.

To solve it, you can simply return an object instead of two values:

resolve({
    data: distributionData,
    labels: distributionLabels
});

And then here, you can do something like:

setTypeDistributionBar(result).then(function(r) {
   $scope.distributionLabels = r.labels;
   $scope.distributionData = r.data; 
   // ...
});
laurent
  • 88,262
  • 77
  • 290
  • 428