0

I'm still trying to get my head around all this and I'm obviously missing some fundamental concepts.

In my code I have a scenario whereby I want to call several functions in a row and when they've all completed, wrap it up with a finishing routine where subtotals are calculated and a pie chart is updated.

Although the functions I call themselves aren't asynchronous, they do contain ajax calls which are, hence why I want to wait for all of them to finish before I calculate totals and update the chart.

So I tried doing:

    var fnArray= [];
    fnArray.push(genericCalc("use_", true, false, false));
    fnArray.push(doArticleImpacts(false));
    fnArray.push(genericProjectCalc("tpD2C_", false, false));
    fnArray.push(genericCalc("eol_", true, false, false));
    fnArray.push(calcPackaging(false));

    var calcPromise = Q.all(fnArray);

    return calcPromise
            .then(calcsDone)
            .fail(calcsFailed);

    function calcsDone() {
        calcTotals(); 
        setChart(selectedRow());
    }

    function calcsFailed() {
        logger.logError("Failure in calculations", "", null, true);
    }

...but using the above code and using the script debugger with a stop on the "return calcPromise" line, the fnArray is set to "0:undefined, 1:undefined, 2:Object, 3:undefined, 4:Promise" even before the promise is activated.

I understand that this is obviously something to do with my functions, but I don't really understand what I need to do differently. The functions all vary slightly, but are fundamentally something like:

var genericCalc = function (calcPrefix) {
    var res_Array = ko.observable(); //holds returned results
    prjArticleArray.forEach(function (thisArticle) {

        var calcPromise = calcEOL(res_Array, thisArticle);  //another function containing async ajax call

        return calcPromise
                .then(calcsDone)
                .fail(calcsFailed);

        function calcsDone() {
            //do calculation subtotals here and set a knockout observable value
        }

        function calcsFailed() {
            logger.logError("Failure in " + calcPrefix + "calculation", "", null, true);
        }

    });
};

What is it that makes some of the functions "undefined", some "object" and some "promise" in my array that I want to use for Q.all? Do I have to have "Q.resolve" in the "calcsDone" part of the functions I'm calling?

I've seen other questions/answers on stackoverflow along similar lines, but they seem to always be calls directly to async calls and my fist level functions I'm stacking up in the promise aren't... should I not be using this structure for non-async calls or just add "setTimeout" to my function calls to make them async?

TheMook
  • 1,531
  • 4
  • 20
  • 58
  • 1
    Your `genericCalc` function doesn't return anything. So it will always be undefined in `fnArray`; – Matt Way Feb 27 '14 at 13:20

1 Answers1

2

I understand that this is obviously something to do with my functions, but I don't really understand what I need to do differently

You need to return promises from them. Your current genericCalc function does not even contain a return statement (only the forEach callback function inside it does), so it does return undefined.

If you need to wait for the result of each article, use Q.all like you have already done with fnArray (which is a promiseArray actually). In genericCalc it should look like this:

var genericCalc = function (calcPrefix) {
    return Q.all(prjArticleArray.map(function (thisArticle) {
        var calcPromise = calcEOL(res_Array, thisArticle);  //another function containing async ajax call
        var result = calcPromise
                .then(calcsDone)
                .fail(calcsFailed);
        return result;
        …
    });
};
var res_Array = ko.observable(); //holds returned results
…
//do calculation subtotals here and set a knockout observable value

That is a bad idea. You should not use global variables that are set somewhen and use promises just for propagating changes, but the promises should represent these values. This leads to a better, functional programming style.

So instead of setting a global variable, do return the result from your calcsDone function, and it will resolve the result promise with that value.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • OK, I'm trying to get my head around this... so in the code that CALLS the genericCalc function do I need to have something like `var myVar = genericCalc(params...)` or how else does the value get back? I don't understand your line, "it will resolve the result promise with that value" sorry... (by the way, the observables are entity objects created by Breezejs, I'm not setting up a whole lot of observables manually) – TheMook Feb 27 '14 at 14:32
  • You can access the value in the `then` callback: `genericCalc(params).then(function(articleResults){…})`. Instead of manually `.resolve()`ing a deferred, it is enough to `return` the value from the `then` callbacks to access it in the next one. The observables are probably needed for your breeze infrastructure, so you might not completely omit them (however, I don't know much about them). My point was that you shouldn't use a global structure to store values in one procedure and to access them from the next one, but that you should pass the results [only] through the promise resolve values. – Bergi Feb 27 '14 at 15:39
  • I'm not getting it, sorry. This is too far removed from my traditional .net knowledge so it's a mystery. Is there an online resource you could possibly point me at that would help me get a better fundamental understanding do you think please? I never seem to be able to find any relevant examples when I look. – TheMook Feb 27 '14 at 16:04
  • I just found https://github.com/kriskowal/q/wiki/General-Promise-Resources, which seems to be an excellent start. If they had not been in that list already, I'd have linked you the two top entries from the "blog posts" section :-) – Bergi Feb 27 '14 at 16:09