0

Pretty straightforward here, but it seems I'm missing some nuance. I'm creating an array of promises, and using Q.all() to ensure that all inserts into a db are finished before shipping a response back to the client. However, it's never being called. How can I get the response to be sent?

    var promises = [];

    for(var i=0; i < rows.length; i++){
        // Insert new row here
        var def = Q.defer();
        promises.push(def.promise);

        var query = connection.query(createQuery(rows[i]), function(err, rows) {
          if(err) {
            console.log(err);
            return;
          }
        });

        query.on('end', function(){
            console.log(def);
            def.resolve();
        });


    }

    Q.all(promises).then(function() {
       res.json({success:true, rows: rows.length});
    });
freedomflyer
  • 2,431
  • 3
  • 26
  • 38
  • possible duplicate of [Javascript closure inside loops - simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Bergi Mar 05 '14 at 13:03
  • Wait, *exact* duplicate of [adding promises to an array of promises in a for loop](http://stackoverflow.com/q/21885073/1048572) – Bergi Mar 05 '14 at 13:06

2 Answers2

1

Your call to def.resolve() is unpredictable. If your queries take time, all of the queries will just resolve the last def again and again. You need to maintain an array of defs too.

var promises = [], defs = [];

Now figure out a way to connect the individual defs with the correct queries and resolve that def when the query is resolved.

Or you can call your query function in an anonymous function. You won't need the defs array in this case.

function(def) {
    var query = connection.query(createQuery(rows[i]), function(err, rows) {
        if(err) {
            console.log(err);
            return;
        }
    });

    query.on('end', function(){
        console.log(def);
        def.resolve();
    });
}(def);

OR Take that part out and make it into a function.

function queryRow(row, def) {
    var query = connection.query(createQuery(row), function(err, rows) {
        if(err) {
            console.log(err);
            return;
        }
    }); 

    query.on('end', function(){
        console.log(def);
        def.resolve();
    });
}

queryRow(rows[i], def);

Alternatively, you can use q's denodeify or nfbind function and turn your mysql query function to one that returns a promise.

Mukesh Soni
  • 6,646
  • 3
  • 30
  • 37
  • I took what you said, and then used a resolveIndex outside the for loop. Then, when each promise is resolved, I increment resolveIndex such that the next time a def is resolved it will resolve the correct index in the def[] array. Sound alright? – freedomflyer Mar 05 '14 at 06:23
  • Nope. Just incrementing won't do it because the queries might not be resolved in order. You need to create another function which in turn returns your query function. And there you need to pass the index. `function getQueryFunc(row, index){ return function(){//return query function}}`. There you can keep hold of the index because of closure. – Mukesh Soni Mar 05 '14 at 06:26
  • I am not seeing this clearly. Do you mind posting some high level code in your answer? – freedomflyer Mar 05 '14 at 06:31
  • 1
    Have posted code. But i would suggest you used q.denodeify or q.nfbind – Mukesh Soni Mar 05 '14 at 06:49
0

Use .map() and .nfbind

var queryAsync = Q.nfbind(connection.query, connection);
Q.all(rows.map(function(row)) {
    return queryAsync(createQuery(row));
})).then(function() {
    res.json({success:true, rows: rows.length});
});
Esailija
  • 138,174
  • 23
  • 272
  • 326