Node mongoose.js - how NOT to use a callback function

I am have just finished the lesson https://www.freecodecamp.org/learn/apis-and-microservices/mongodb-and-mongoose/perform-classic-updates-by-running-find-edit-then-save and figured it out. Below is the solution. I had trouble working it out though because on the final line of the code I was trying to use docs.save() instead of docs.save([callback]). The reason I was trying to use it without a callback is simply because I wanted to, and the documentation stated that specifying a callback was optional. According to the documentation for this command here (and almost all other node commands that support callback) callbacks are optional so I want to understand why it wouldn’t work for me. Below this code (which uses the callback) I will show the code i used without a callback which failed.

var findEditThenSave = function(personId, done) {
  var foodToAdd = 'hamburger';

 
Person.findById(personId, function (err, docs) {
  if (err) return (err); 
  docs.favoriteFoods.push(foodToAdd);
  console.log(docs);

      docs.save(function (err, data) {
      if (err) return (err);
      done( null, data);
      }); 
  }); 
 

};

So where we have:

docs.save(function (err, data) {
      if (err) return (err);
      done( null, data);
      }); 
  });

I replaced this code with just

done (docs.save());

But it didn’t work. Should it have or am I misunderstanding how to use this api without a callback?
I even tried console.log(docs.asve()) to rule out if I’d made other mistakes in my code and it didn’t work either. It said something about a promise??

You need to wait for a data from Mongo and then to pass it to done, right? When using like this:

You’re passing a promise for the data, not the data itself. Apparently done function isn’t happy with that :slight_smile:

So the only way is to wait for the data. And here you have 3 options:

  1. Callback
  2. Promise.then()
docs.save().then((data) => done(null, data));
  1. async function
Person.findById(personId, async (err, docs) => {
  if (err) return (err);
  // btw, starting from next line you're entering territories of black magic
  // and probably should not do that outside curriculum
  docs.favoriteFoods.push(foodToAdd);

  const data = await docs.save();
  done(null, data);
});

thanks Snido.

So am I right in saying then that the docs.save() is an async function?

I have came to this conclusion due to the fact that it doesn’t wait for the results (which is a characteristic of a callback or async function). To elaborate on this - before I started looking at Node.js my understanding of normal coding was ‘blocking’ and non async. When I then started learning Node I found the concept of non blocking code. But, my understanding of this is that if you want some code to be non blocking you had to write it a specific way, namely it being a callback function or async function (or something else I haven’t learnt yet). When I saw docs.save() I thought it would natively ‘block’, and by adding an optional callback (accord to it’s doco) this would turn it into the ‘non-blocking’ equivalent - in other words my understanding is that all code is ‘blocking’ by default until you define it otherwise with a CB or async function (is this understanding correct?)

From what you have described (and demonstrated) of docs.save() it appears to be non- blocking, therefore my conclusion is that it must be an async function. Would this assessment be correct?

Yes, this is correct. If you don’t provide callback, it returns promise. You also have to note that it’s a custom Mongoose logic, not every function that expect callback (including in NodeJS) would do that. This is just that way Mongoose API works, they provide backward compatibility for “old” code while giving options for writing “new” code.

what is “old” and “new” code? callbacks and promises? are promises like an evolution of callbacks?

It’s just a new slightly more readable syntax, under the hood it’s the same thing. This kind of stuff is also called “syntactic sugar”.

1 Like