A bit confused with promises/chained functions in

2019-09-17 13:39发布

问题:

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?

回答1:

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.