0

I am struggling a little with how to call asynchronous functions in a serial manner. In particular, functions which incorporate mongoose calls to the database. I have a class definition which includes two methods: (MovieFile.exists) checks if a certain record exists in the database; (MovieFile.save) saves a record in the database. Ideally I want to be able to save the record (MovieFile.save()) after confirming whether or not it exists in the database (MovieFile.exists()).

Ideally I want to do something like this:

// Create instance of MovieFile object.  
var movieFile = new MovieFile(mongoose);
if (movieFile.exists() === false) {
  movieFile.save();
}

Unfortunately the asynchronous nature on mongoose makes this not possible. I have been playing around with Step and Async control flow frameworks. Unfortunately I just can't get my head around how to use such frameworks in this case. I would be grateful if someone could tell me how to put the above code into either Step or Async. I think the issue is that the asynchronous mongoose calls are themselves embedded within a method (See: MovieFile.exists and MovieFile.save). I realise that use of such a framework may be overkill in this example, but I am trying to use this as a learning exercise.

function MovieFile(mongoose) {
  var Movie = mongoose.model('Movie');

  this.exists = function() {
    // Confirm necessary object variables have been set.
    if (typeof this.originalFileName === 'undefined') {
      throw error = new Error('Variable originalFilename has not been set for MovieFile.');
    }
    if (typeof this.fileSize !== 'number') {
      throw error = new Error('Variable originalFilename has not been set for MovieFile.');
    }
    // Check database for existing record.
    Movie
      .find({ originalFileName: this.originalFileName, size: this.fileSize })
      .exec(function(error, results) {
        if (error) {
          throw error;
        }
        else if (results.length === 0) {
          return false;
        }
        else if (results.length === 1) {
          return true;
        }
        else {
          throw error = new Error('More than one record for this movie record exists.');
        }
      });
  };

  this.save = function() {
    // save movie to database.
    var values = {
      name: this.getName(),
      machineFileName: this.getMachineFileName(),
      originalFileName: this.getName(),
      size: this.getFileSize(),
    };
    var movie = new Movie(values);
    movie.save(function(error, data) {
      if (error) {
        console.log(error);
      }
      else {
        console.log('Movie saved.');
      }
    });
  };
};
Benjen
  • 2,835
  • 5
  • 28
  • 42

1 Answers1

2

You have to pass a callback to your exists function, wich will return the boolean true or false.

Exemple :

this.exists = function(callback) {

   // your code

   callback(true);

}); 

Then :

movieFile.exists(function(exists) {

     if (!exists) boomovieFile.save();

});

As you said, this is due to the asynchronous mongoose calls. So, you will have to replace your return statements by callbacks.

I've never used Step or Async control flow frameworks. But I think, you don't need to rely on those modules for this kind of simple case. In my opinion, you should only consider using those modules when you will have to deal with a lot of callbacks.

Edit

As I'm new to control flow frameworks, I've found this article which describes well the concept and shows how to build your own. That's great for learning purposes.

This article will give you an example on how to manage a queue using the async module.

This post list some other control flow modules, with few examples. The first answer also show how to avoid nested callbacks, by decoupling on small functions. This is how I personnaly manage my apps, which works quite well in my case.

But I would be curious to hear more about control flow techniques, so let me know if you found great ressources.

Community
  • 1
  • 1
Adrien Schuler
  • 2,395
  • 1
  • 21
  • 32
  • Thanks for the quick reply. Actually your suggested method was what I have already implemented as a solution to this particular issue. However, I was kind of trying to use this as a learning exercise, on using a control flow framework. Call me pedantic, but I want to know how to implement this using Async. I am trying to understand how such a framework works in this kind of scenario so that I might use it in the future. – Benjen Jul 05 '12 at 08:14
  • As I can't try those modules now, I've updated my post with some links. – Adrien Schuler Jul 05 '12 at 09:59
  • I finally worked it out. I had overlooked one very vital, and very obvious thing; that the functions be asynchronous in nature. Since MovieFile.exists() doesn't take a callback as an argument, it will never work with frameworks like Async. So in fact my initial suspicions were correct, the issue was due to having an asynchronous function embedded within a synchronous function, which now is so darn obvious. Your links help me get my head around it all. I feel so much more enlightened. Thanks. – Benjen Jul 05 '12 at 13:29
  • Okay this makes sense. Glad I could helped, I also learned here. You should mark this thread as resolved. – Adrien Schuler Jul 05 '12 at 13:40