http call in backbone promise

Hi I have a backbone web app using Jquery and NodeJs/mongo as the server side framework. I'm having problems with making a http get call with a foreah loop and the results of the get call being iteratively added to each row of the loop.

                        var eventid = this.model.get("_id");

         var inPromise = $.get("/registrants/list?eventid="+eventid,null,null,"json").then(
            function (result){

                var temp;
                var finalVal = '';
                var tempfinalVal = "";
                var loop = 0
                    percentage = 0;

                $.each(result.registrants,function(index,registrant){
                    temp =    JSON.parse(registrant.fields);

                    for (var key in temp) {

                        if(key =="Email"){

                           if(temp[key] != ""){
                                $.get("/stats/registrant?userid="+temp[key]+"&eventid="+eventid,null,null,"json").then(function(result2){

                                    percentage = (result2.Stats.type ===undefined || result2.Stats.type ==null) ? "0": result2.Stats.type;

                                    finalVal +=percentage+"\n";


                                }).fail(function(){

                                    percentage = "0";
                                });
                            }

                        }else if(key =="eventid"){    

                            loop++;
                            finalVal = finalVal.slice(0, - 1);
                            finalVal +='\n';

                        } 

                        finalVal +=temp[key] + ',';


                    }


                });

                //promises.push(inPromise);

            }
        ).done(function(finalVal){

            $("#webcast-download-registrants-tn").attr("href",'data:text/csv;charset=utf-8;filename=registration.csv",'+encodeURIComponent(finalVal));

                console.log("DONE");
        }).fail(function(){
                console.log("fail");
        });


       // promise.done(function () {
       //                  console.log(" PROMISE DONE");
       // });

So I have the loop through a collection and the last item of the docuemnt gets a content froma nother http call and when all is fone it will create a CSV file. The problem is that THE "DONE" text echos firts then the "CALL" text is displayed

Rick, your problem is not the simplest due to :

  • the need for nested asynchronous gets
  • the need to build each CSV data row partly synchronously, partly asynchronously.
  • the need for a mechanism to handle the fulfilment of multiple promises generated in the inner loop.

From what you've tried, I guess you already know that much.

One important thing to note is that you can't rely on for (var key in temp) to deliver properties in any particular order. Only arrays have order.

You might try something like this :

var url = "/stats/registrant",
    data = { 'eventid': this.model.get('_id') },
    rowTerminator = "\n",
    fieldNames = ['firstname','lastname','email','company','score'];

function getScore(email) {
    return $.get(url, $.extend({}, data, {'userid':email}), null, "json").then(function(res) {
        return res.Stats ? res.Stats.type || 0 : 0;
    }, function() {
        //ajax failure - assume score == 0
        return $.when(0);
    });
}

$.get("/registrants/list", data, null, "json").then(function(result) {
    var promises = [];//An array in which to accumulate promises of CSV rows

    promises.push($.when(fieldNames)); //promise of CSV header row

    if(result.registrants) {
        $.each(result.registrants, function(index, registrant) {
            if(registrant.fields) {
                // Synchronously initialize row with firstname, lastname, email and company 
                // (omitting score for now).
                var row = fieldNames.slice(0,-1).map(function(fieldName, i) {
                    return registrant.fields[fieldName] || '';
                });
                //`row` remains available to inner functions due to closure

                var promise;
                if(registrant.fields.Email) {
                    // Fetch the registrant's score ...
                    promise = getScore(registrant.fields.Email).then(function(score) {
                        //... and asynchronously push the score onto row
                        row.push(score);
                        return row;
                    });
                } else {
                    //or synchronously push zero onto row ...
                    row.push(0);
                    //... and create a resolved promise
                    promise = $.when(row);
                }
                promises.push(promise);//Accumulate promises of CSV data rows (still in array form), in the correct order.
            }
        });
    }
    return $.when.apply(null, promises).then(function() {
        //Join all the pieces, in nested arrays, together into one long string.
        return [].slice.apply(arguments).map(function(row) {
            return row.join(); //default glue is ','
        }).join(rowTerminator);
    });
}).done(function(str) {
    $("#webcast-download-registrants-tn").attr("href",'data:text/csv;charset=utf-8;filename=registration.csv",'+encodeURIComponent(str));
    console.log("DONE");
}).fail(function() {
    console.log("fail");
});

partially tested

See comments in code for explanation and please ask if there's anything you don't follow.