简体   繁体   中英

JavaScript Recursion with promises — order of execution error

Building a method to create markup for a web app off canvas navigation. I am making async callback to another service that returns children of the parent menu node (see code below):

function GenerateMarkup(Terms, n) {
    var termsEnum = Terms.getEnumerator();
    var html = "<ul>";

    // Process top level terms
    while (termsEnum.moveNext()) {
        var currentTerm = termsEnum.get_current();        

        html += "<li>"

        if (currentTerm.get_termsCount() > 0) {
            var childcall = function() {
                var deferred = $.Deferred();

                html += "<a href=\"#\">" + currentTerm.get_name() + "<br><span>" + currentTerm.get_description() + "</span></a>";
                SPTermStore.GetTermsFromTermSet(currentTerm).then(function(termSet) {
                    if (typeof termSet !== undefined) {
                        deferred.resolve(GenerateMarkup(termSet, n++));
                    }
                    else
                        deferred.reject("something bad happened");
                });
                return deferred.promise();
            };

           $.when(childcall()).done(function(markup) {
                html += markup;
            });
        } // end if
        else
            html += "<a href=\"#\">" + currentTerm.get_name() + "</a>";

        html += "</li>"
    } // end while

    html += "</ul>";
    console.log("GenerateMarkup (" + n + "): " + html);
    return html;
} // end function

The issue is the order the markup is generated is not right; in a normal synchronous the recursive call to GenerateMarkup would complete, but in this situation I am trying to wait for the returned promise (ie the call to GenerateMarkup to complete) so I can append the html. The idea is as it iterates through the while, top level nodes will have their child nodes processed etc.

If I look at the console.log output this is what I get; the problem is the first listed markup below is what is returned to the page and not the combination of the below.

GenerateMarkup (0): <ul><li><a href="#">About<br><span>Our Company</span></a></li><li><a href="#">Portfolio<br><span>Our Properties</span></a></li><li><a href="#">Corporate Responsibility<br><span>Our Committment</span></a></li></ul>
GenerateMarkup (0): <ul><li><a href="#">Careers</a></li><li><a href="#">Core Values</a></li><li><a href="#">Governance</a></li><li><a href="#">History</a></li></ul>
GenerateMarkup (1): <ul><li><a href="#">Core Market Strategy</a></li><li><a href="#">Our Properties</a></li></ul>
GenerateMarkup (2): <ul><li><a href="#">Community Involvement</a></li><li><a href="#">CSR Report</a></li><li><a href="#">Diversity</a></li><li><a href="#">Sustainability</a></li></ul>

Any help would be appreciated.

Promises are asynchronous so they're not guaranteed to return in the order in which they're promised.

If the ordering is important, consider chaining the promises so that they execute in the sequence that you expect. Promise.then can be chained.

One possibility would be to loop through the termsenumerator, and load these into a deferred array. Then apply the $when, later. See sample below:

 var deferreds = [];
 for (var i = 0; i<termsenumerator; i++) {              
      deferreds.push(Grab data you want and push it into this array);             
 }

//Now that we have all the results... (deferred), process these.
        $.when.apply($, deferreds).done(function () {

            var resultdata = [];

            for (var i = 0; i < arguments.length; i++) {
                var daydata = arguments[i][2];                   
                applymarkupWData(resultdata.responseJSON.d,i);


            };

        });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM