简体   繁体   中英

I wish to understand the behavior of promise inside and outside callback function

I'm fairly new to NodeJS and am trying to understand async, await, and promises. The likes variable is undefined if I take resolve() outside of the callback scope of 'con.query' (still inside the new Promise scope). It is only populated if resolve() is inside the callback. Why does this happen? Examples:

//WORKS
async function findLikes(q, req, res){

  var likes_q = `SELECT * FROM PublicStoryLike WHERE authorId = 
${req.body.token_id}`;
  var likes;
  var lookup = {};

  //const query = util.promisify(con.query).bind(con);

  const query = new Promise((resolve, reject) => {
    con.query(likes_q, (err, result) => {
    likes = JSON.stringify(result);
    resolve();    //INSIDE con.query
  });

  })

//DOESN'T WORK
async function findLikes(q, req, res){

  var likes_q = `SELECT * FROM PublicStoryLike WHERE authorId = 
${req.body.token_id}`;
  var likes;
  var lookup = {};

  //const query = util.promisify(con.query).bind(con);

  const query = new Promise((resolve, reject) => {
    con.query(likes_q, (err, result) => {
        likes = JSON.stringify(result);

     });
     resolve(); //OUTSIDE con.query 
  })

The problem in the second code block is that you are resolving the promise immediately. You are not waiting for the con.query method to finish.

Since you want to use async/await and you seem to be working with util.promisify I would recommend the following:

async function findLikes(q, req, res) {
  const likes_q = `SELECT * FROM PublicStoryLike WHERE authorId =  ${
    req.body.token_id
  }`;

  const query = util.promisify(con.query).bind(con);
  const likes = await query(likes_q);

  // ....
}

You can see you don't need to manually create a Promise. That's what util.promisify will do for you. You can simply just await for it.

EDIT: By further looking at your code, I see that findLikes looks like an express route. Generally, I don't recommend coupling your web-request logic with your data access layer.

I would suggest you separate them, by making findLikes a standalone async function:

async function findLikes(authorId) {
  const likes_q = `SELECT * FROM PublicStoryLike WHERE authorId =  ${
    authorId
  }`;

  const query = util.promisify(con.query).bind(con);

  return await query(likes_q);
}

And later on in your route:

async function findLikesRoute(q, req, res){
  const likes = await findLikes(req.body.token_id);
  //... work with the data
}

This will give you the benefits of separation of concerns. For example, what would you do if let's say you would want to change the way you expose data?. Instead of exposing it through this express route, you now want now to expose it through GraphQL?

If you have your data access logic separated, it would be really easy.

Let's forget about promises. The missunderstanding is rather about callbacks here: Your case can be illustrated as:

  function works(callback) {
     setTimeout(function() {
        console.log("inside timeout");
        callback("result");
     }, 1000);
  }

  function doesnt(callback) {
    setTimeout(function() {
      console.log("inside timeout");
    }, 1000);
    callback();
  }

  works(function(result) { console.log("called back"); });

In the working version, you call back the outer callback when the inner callback was called, meaning the result arrived from the db in your case.

In the non working version you call back the callback directly, yet the timer (or the database call) did not finish, therefore the results are not there yet.


You should usually not use global variables, especially when asynchrony is involved. Your likes variable will cause you (and does already) cause you headaches, just remove it completely. Instead, resolve the promise (or call the callback back) with the value needed. That way you can easily detect errors:

  getResult(function(result) {
    callback(result); // works, result is ready
  });

  callback(result); // does not work, throws an error

In your case, that'll be:

 async function findLikes(q, req, res){

   const query = `SELECT * FROM PublicStoryLike WHERE authorId = ${req.body.token_id}`;



   return new Promise((resolve, reject) => {
    con.query(query, (err, result) => {
       if(err) reject(err) else resolve(result);
    });
   });
 }

 findLikes(/*...*/)
   .then(likes => {
     // Work with likes here!
   });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM