2
votes

Following the suggestions from AngularJS validation and promises, I would like to chain confirmation dialogs and thus validate several steps at once.

Based on data provided by the user, an API call is made to see what all needs to be confirmed by the user. For each step that needs confirmation, prompt the user and let them decide whether to go to next step. If any step returns false, the whole chain should return false.

I've been reading a lot about async JS and promises, but I have to admit I am still fairly new to it. How to properly chain these to get a final true/false for all steps? Note that an API call is needed to determine what all needs to be shown to the user based on provided information, hence fetchSomeData() as first call in the chain.

Any help or suggestions will be greatly appreciated.

fetchSomeData = function() {
    var deferred = $q.defer();
    api.fetchData(param1, param2, param3)
        .then(function(data) {
        deferred.resolve(data.content);
    }, api.errorHandler);
    return deferred.promise;
}
// data = {condition1: false, condition2: true, condition3: true}
// display confirmation dialogs for step 2 and step 3, not step 1 

confirmStep1 = function(data) {
    if (data.condition1) {
        return confirmDialogService.popConfirm('step1').then(function(confirmed) {
            return confirmed;
        }, function() {
            return false;
        });
    } else {
        return $q.when(true);
    }
}

confirmStep2 = function(data) {
    if (data.condition2) {
        return confirmDialogService.popConfirm('step2').then(function(confirmed) {
            return confirmed;
        }, function() {
            return false;
        });
    } else {
        return $q.when(true);
    }
}

confirmStep3 = function(data) {
    if (data.condition3) {
        return confirmDialogService.popConfirm('step3').then(function(confirmed) {
            return confirmed;
        }, function() {
            return false;
        });
    } else {
        return $q.when(true);
    }
}

confirmSteps = function() {
    return fetchSomeData()
        .then(confirmStep1(data))
        .then(confirmStep2(data))
        .then(confirmStep3(data));
}

confirmSteps().then(function(allConfirmed) {
    if (allConfirmed == true) {
        doSomething();
    } else {
        return;
    }
});
1
did you forget to include a question?Kevin B
@KevinB Edited, thanks for the reminder.me_

1 Answers

3
votes

dfsq started writing an answer but deleted his so with his blessing I'm adding my take on it:

confirmSteps = function() {
    return fetchSomeData()
        .then(confirmStep1(data))
        .then(confirmStep2(data))
        .then(confirmStep3(data));
}

This calls functions, it's the same as setTimeout(alert("Hi"),5) you don't want to be calling the functions you want to chain them. Like setTimeout(function(){ alert("Hi"); }, 5);

confirmSteps = function() {
    return fetchSomeData()
        .then(confirmStep1)
        .then(confirmStep2)
        .then(confirmStep3);
}

However, that would pass data to the first promise only and the result of the previous promise to the next one, instead you want to pass data to all three, you can do this by neting one level:

confirmSteps = function() {
    return fetchSomeData().then(function(data){
        var v1, v2;
        return confirmStep1(data).then(function(d){ 
           v1 = d;
           return confirmStep2(data);
        }).then(function(d){
           v2 = d;
           return confirmStep3(data);
        }).then(function(v3){
            return v1 && v2 && v3;
        })
    });
};

This works but it's kind of crufty, instead you can use short circuiting - kind of like how && only evaluates the left hand side if it's falsey. Moreover we can do all error handling in a central location. This would make your code look like.

confirmStep1 = function(data) {
    if (data.condition1) return $q.when(true);
    return confirmDialogService.popConfirm('step1');
};

confirmStep2 = function(data) {
    if (data.condition2) return $q.when(true);
    return confirmDialogService.popConfirm('step2');
};

confirmStep3 = function(data) {
    if (data.condition3) return $q.when(true);
    return confirmDialogService.popConfirm('step3'):
};

confirmSteps = function() {
    var data = fetchSomeData();
    return data.then(confirmStep1).then(function(soFar){ 
         if(!soFar) return false; 
         return data.then(confirmStep2); 
    }).then(function(soFar){ 
         if(!soFar) return false; 
         return data.then(confirmStep3); 
    }).catch(function(){ return false; });
};

As an extra tip this:

fetchSomeData = function() {
    var deferred = $q.defer();
    api.fetchData(param1, param2, param3)
        .then(function(data) {
        deferred.resolve(data.content);
    }, api.errorHandler);
    return deferred.promise;
};

Can simply become:

fetchSomeData = function() {
    return api.fetchData(param1, param2, param3).then(function(data) {
        return data.content;
    }, api.errorHandler);
};