Question

SO.

I have a question in regards to handling Windows Azure Mobile Services asynchronous calls to an Azure database.

Right now, I am posting an object with an array which I am in turn looping over in my WAMS api endpoint.

The loop gets the current object in the array, and pushes it together with an unique ID to the database. This returns the row id from the last inserted row, and in the callback I fetch this as well as instantiate another asynchronous call.
Here I'm then inserting the last inserted row id, together with a bunch of properties from the object in the current loop of the array.

However, this seems to behave weirdly, and insert the same information in all the columns in my second asynchronous call.

for (var i = 0; i < object.array.length; i++) {

    var mssql = request.service.mssql,
        prop1 = object.array[i].prop1,
        prop2 = object.array[i].prop2,
        user  = object.user.userid;

    mssql.query("exec stored_procedure ?, ?", [user, prop1], {
        success: function (res) {

            var insertedid = res[0];

            if (typeof insertedid === 'undefined') {
                return;
            } else {
                insertedid = insertedid.ID;
            }

            mssql.query("exec stored_procedure_2 ?, ?", [insertedid, prop2], {

                //success

            });

        }
    });
}

I am probably doing something completely wrong, because I don't quite like what I've produced, but considering my node.js familiarity, this was it.

Was it helpful?

Solution

Here is a possible implementation with async.js. If you have more than two properties, you will need to generate the "insertPropX" functions dynamically, but this should get you started.

var mssql = request.service.mssql;
var user = object.user;

function insertProp1(obj, cb) {
  mssql.query("exec stored_procedure ?, ?", [user.userid, obj.prop1], {
    success: function (res) {
        var inserted = res[0];
        if (!inserted) {
          return cb('prop1 not inserted for userid: ' + user.userid);
        }
        // pass the ID and the object to the next
        // function in the "waterfall" chain
        cb(null, inserted.ID, obj);
    }
  });
}

function insertProp2(id, obj, cb) {
  mssql.query("exec stored_procedure_2 ?, ?", [id, obj.prop2], {
    success: function (res) {
      var inserted = res[0];
      if (!inserted) {
        return cb('prop2 not inserted for id: ' + id);
      }
      // since this is the last function in
      // the "waterfall" chain, we don't need
      // to pass anything along
      cb(null);
    }
  });
}

// for each object in the array...
async.each(object.array, function iterator (obj, cb) {
  // insert properties in order, passing the
  // insertion ID as an argument
  async.waterfall([
    insertProp1.bind(null, obj),
    insertProp2
  ], cb);
}, function allDone (err) {
  if (err) {
    console.log(err);
  }
  // all done
});

OTHER TIPS

The loop executes faster than the outer queries finish and make their callbacks. As a result, as each callback execute, they are all using the current (and final) value of i rather than the value when the outer query executed.

This is illustrated with a simple loop like this that shows what is happening:

for (var i = 0; i < 10; i++) {
    setTimeout(function(){
      console.log('the value for i is', i);
    },100);
}

You'll see that this little program does NOT print a sequence but instead this 10 times:

the value for i is 10

Using promises (lists several libraries or options) or a tool like async (link to github page) are two potential solutions to this issue.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top