0

I have a situation like:

function smth(data) {
   // save data to db.
   Object.findOne({ _id: ceva['id']}, function(obj) {
     obj.save();
   });

}

This function is called from various async calls. There is a race condition where the second findOne call runs before a previous save() runs.

Is there a way to work around this? Maybe using the async library to run things in series?

2 Answers 2

1

You can make use one of async control flows to ensure every iteration of smth() happens in order.

If you're not in favor of using a flow control library, you can easily achieve series execution of each event. Consider following code snippet:

function smth(data, cb) {
  // save data to db.
  findOne({
    id: data.id
  }, function (err, obj) {
    if (!err && obj) {
      savedb(obj, cb);
    } else {
      cb(err);
    }
  });
}

function findOne(filter, cb) {
  // simulate find
  setTimeout(function () {
    cb(null, {
      id: filter.id,
      name: 'test',
      role: 'test'
    });
  }, 500);
}

function savedb(obj, cb) {
  //simulate db save
  setTimeout(function () {
    cb(null, obj);
  }, 500);
}

// iterations count
var count = parseInt(process.argv[2], 10) || 3;

(function iterate(i) {
  console.log(i);
  if (i === count) {
    // iterations complete
    process.exit(1);
  }

  var data = {
    id: 123 + i
  };

  smth(data, function (err, res) {
    console.log(err || res);
    iterate(++i);
  });
})(0);
Sign up to request clarification or add additional context in comments.

1 Comment

How is smth() getting called, multiple times in a loop or from multiple places? If it's a loop, above routine or async.series, async.waterfall or async.compose() can be made to work. If it's being triggered from multiple places, you will need to implement a task queue, use async.queue or any task queue impl will do. async explains these tasks w/ examples, can you try them and see if they work for you?
1
//make this follow async conventions with callback argument last
function smth(data, callback) {
   //pseudocode database API here
   db.save(data, function (error) {
     if (error) {
       callback(error);
       return;
     }
     Object.findOne({ _id: ceva['id']}, function(obj) {
       obj.save(callback);
     });
   });
}

That's the basic callback approach. You can use async.js if you like to clean it up a bit or study callbackhell.com for more ways to avoid the nested functions.

2 Comments

The problem is smth() can be called multiple times. For each call I need to get an object and save it. Sometimes two calls end up getting the object at the same time, and the last save() wins the db. The data in the first call is lost.
So going from your snippet to my answer is a good discrete step as this is now at least correct async code that a single caller can invoke and get race-free behavior. To enforce sequential operation of multiple concurrent calls, you need async.queue as Vinayak points out in his comment above.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.