Create a gist now

Instantly share code, notes, and snippets.

@naholyr /dafuq.md
Last active Aug 29, 2015

What would you like to do?
That one was nice

The bug

/*
  The model is something like:
  Person: { name: String }
  Car: { name: String, driver: {type: Schema.Types.ObjectId, ref: 'Person'} }
*/

// Somewhere we do this…
async.auto({
  owner: Model.findOne.bind(Model, {name: 'Owner'}),
  driver: car.populate.bind(car, 'driver')
}, function (err, o) {
  // … but we NEVER come here
  console.log('The driver is:', car.driver.name);
});

DAFUQ?

The fix

Instinctively, we tend to remove implicit, here meaning removing partial applications:

async.auto({
  owner:  function (cb) { Model.findOne({name: 'Owner'}, cb) },
  driver: function (cb) { car.populate('driver', cb) }
}, function (err, o) {
  // AND NOW IT WORKS!
  console.log('The driver is:', car.driver.name);
});

But why???

async.auto

The real signature of async.auto functions is function (cb, other_results):

async.auto({
  owner:  function (cb, results) { Model.findOne({name: 'Owner'}, cb) },
  driver: function (cb, results) { car.populate('driver', cb) }
}, function (err, o) {

Function#bind

That means our previous code with bind should translate like this:

async.auto({
  owner:  function (cb, results) { Model.findOne({name: 'Owner'}, cb, results) },
  driver: function (cb, results) { car.populate('driver', cb, results) }
}, function (err, o) {

Mongoose Document#populate

The documentation can be a bit vague, but populate can take any number of arguments. The most important one in reality is the last one. If it's a function then the query will be immediately executed and result passed to this callback, else it will return a Query to be executed later.

car.populate('driver', cb, results)
                            ^
                            This is not a function

Yep.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment