0

What is the difference in these calls, why in first case my console logs 'undefined', and in second I get the right answer (an integer).

When calling count() - undefined.

function count() {
  let answer;
  Url.findOne({}).sort({short_url:-1}).exec(function (err,ur) { if (err) return err; answer = ur.short_url });
  console.log(answer);
}

When calling count() - an integer.

function count() {

  Url.findOne({}).sort({short_url:-1}).exec(function (err,ur) { if (err) return err; console.log(ur.short_url) });

}

In general, I want to achieve that such a call (count()) would return highest value of 'short_url'.

1

3 Answers 3

1

The findOne call is asynchronous. So the console.log statement is called before the findOnde statement returned.

The second one is you are using a callback. This means when the findOne function finishes call this function. And this time it is going to work.

But as a best practice I suggest you to use async and await if you are using ES6 javascript.

async function count() {
   let answer;
   await Url.findOne({}).sort({short_url:-1}).exec(function (err,ur) { if (err) return err; answer = ur.short_url });
   console.log(answer);
}
3
  • Actually.. Below Asaf Aviv answer I commented that your solution works well in my case, but it only does in some situations. Please see the link. In this case I get undefined. If I lose the 'shortInt' variable, and console.log(result) straight in .then(...) call, I get the correct value. What's up with that? Basically I need to make sure that I get the right answer, because later on in the code I will increment it by one and save new record with the new value. Commented Oct 26, 2018 at 10:10
  • @NeNenne Tnx, but I have checked out the link and it would print undefined. It is because the function count() is defined async. It means it is still returning a promise. Check this link... medium.com/front-end-hacking/… Commented Oct 26, 2018 at 10:19
  • Yeah, I've been reading this and various other sources and to my understanding your solution does what is being described there. It should wait till the value is resolved, but apparently something's missing. Commented Oct 26, 2018 at 11:01
0

Bereket is correct.

You can also use callbacks,

function count() {
   let answer;
   await Url.findOne({}).sort({short_url:-1}).exec(function (err,ur) { 
       if (err) return err; 
       answer = ur.short_url
       console.log(answer);
    });
}

Or with promises.

function count() {
   let answer;
   await Url.findOne({}).sort({short_url:-1}).exec().then((ans) => {
       console.log(ans);
   }).catch(err => {
       console.log(err);
   });
}

However count() will still be asynchronous so it will not print immeditiely.

You should look into asynchronous methods more here

0
0

It would be useful to read further on closures async nature of javascript, where control flow, closures, scope, are the keys to understand how async code executes in the future

function count() {
  //count function scope

  let answer; //undefined at execution

  Url.findOne({}).sort({short_url:-1}).exec(function (err,ur) {

   //callback function scope 

   if (err) return err; 
  //answer is still undefined

  answer = ur.short_url 

  //answer is assigned ur.short_url only in the callback scope 
  //answer is defined here, and will print the content
  console.log(answer);

  });
  //back to count function scope
  //answer is still undefined in this scope (called function scope)
  console.log(answer);
   }

If you want to return the value of your query you can use a return statement with async

async function count() {
  const answer = await Url.findOne({}).sort({short_url:-1});

  console.log(answer);      //logs answer

  return answer;
}

It's also important to know that you can await an async function execution only from within an async function context

Not the answer you're looking for? Browse other questions tagged or ask your own question.