【问题标题】:I wish to understand the behavior of promise inside and outside callback function我想了解回调函数内外promise的行为
【发布时间】:2019-06-12 20:35:28
【问题描述】:

我对 NodeJS 还很陌生,正在尝试理解 async、await 和 Promise。如果我将 resolve() 置于“con.query”的回调范围之外(仍在新的 Promise 范围内),则 likes 变量未定义。仅当 resolve() 在回调中时才会填充它。为什么会这样?示例:

//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 
  })

【问题讨论】:

  • @mark resolve 在这两种情况下都在范围内。
  • 谢谢@JonasWilms——我的错。编辑缩进以使其更清晰。

标签: javascript node.js asynchronous async-await


【解决方案1】:

让我们忘记承诺。这里的误解是关于回调:您的案例可以说明为:

  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"); });

在工作版本中,您在调用内部回调时回调外部回调,这意味着结果来自您的情况下的数据库。

在非工作版本你直接回调回调,但定时器(或数据库调用)没有完成,因此结果还没有。


您通常不应该使用全局变量,尤其是在涉及异步时。您的 likes 变量将导致您(并且已经)使您头疼,只需将其完全删除即可。相反,resolve 具有所需值的承诺(或回调回调)。这样您就可以轻松检测错误:

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

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

在你的情况下,那将是:

 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!
   });

【讨论】:

    【解决方案2】:

    第二个代码块中的问题是您正在立即解决承诺。您不是在等待con.query 方法完成。

    由于您想使用 async/await 并且您似乎正在使用 util.promisify,我建议您使用以下方法:

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

    您可以看到您不需要手动创建 Promise。这就是util.promisify 将为您做的事情。您只需 await 即可。

    编辑:通过进一步查看您的代码,我发现findLikes 看起来像一条快速路线。一般来说,我不建议将您的网络请求逻辑与您的数据访问层耦合。

    我建议您将它们分开,将 findLikes 设为独立的异步函数:

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

    稍后在您的路线中:

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

    这将为您带来关注点分离的好处。例如,如果假设您想要更改公开数据的方式,您会怎么做?您现在不想通过这种快速路由公开它,而是现在希望通过 GraphQL 公开它?

    如果您将数据访问逻辑分开,那将非常容易。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多