How to use methods containing mongoose functions with control flow frameworks (e.g. Async or Step)

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.');
      }
    });
  };
};

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.