【问题标题】:Understanding difference in two async code snippets了解两个异步代码片段的区别
【发布时间】:2019-04-16 18:26:45
【问题描述】:

我试图深入了解 Stephen Grinder 的高级 NodeJS 概念。

Stephen 试图教授 redis 的基础知识,他做了这样的事情

app.get('/api/blogs', requireLogin, async (req, res) => {

    //This time we are setting 
    const redis = require('redis')
    const redisURL = 'redis://127.0.0.1:6379';
    const  client = redis.createClient(redisURL);
    const util = require('util')
    client.get = util.promisify(client.get)
    //We are checking if we have ever fetched any blogs related to the user with  req.user.id
    const cachedBlog = await client.get(req.user.id) 
    //if we have stored list of blogs, we will return those 
    if (cachedBlog) {
      console.log(cachedBlog)
      console.log("Serving from Cache")
    return res.send(JSON.parse(cachedBlogs))
    } //this is JSONIFIED as well so we need to convert it into list of arrays

    console.log("serving from Mongoose")
    //if no cache exsist 
    const blogs = await Blog.find({_user: req.user.id})
    //blogs here is an object so we would need to stringfy it 
    res.send(blogs);
  client.set(req.user.id, JSON.stringify(blogs))

  })

如果我们更改顺序,它可以正常工作,但在最后两行中

 client.set(req.user.id, JSON.stringify(blogs))
 res.send(blogs);

它不显示我的博客。

由于在 API 内部,我正在考虑将它们都异步运行,我认为顺序无关紧要。

谁能告诉我我错过了什么或无法理解什么?

【问题讨论】:

  • 所以也不例外?
  • 显示是什么意思?从 res.send 还是在下一次调用 console.log 中?
  • @Tzomas 来自 res.send

标签: node.js asynchronous redis


【解决方案1】:

由于 OP 要求理解差异,而不是修复代码:

express 运行请求处理函数并捕获同步错误(它们变成 http500 错误)。它不会对从异步函数返回的承诺做任何事情,并且不会在内部使用等待,因此您不会免费获得异步函数的错误处理。所有异步错误都需要在内部捕获并传递给next 回调或通过发送适当的状态代码和错误正文在您的代码中处理。

当发生错误时,JS 停止并且不再执行函数中的任何行。因此,如果从位于res.send 之前的client.set 引发错误,则带有 send 的行将不会运行并且不会发送任何响应。浏览器应该继续等待响应直到超时。

反过来——你在错误之前发送响应,所以你得到了页面,但是响应没有结束(我假设连接保持打开,好像后端要发送更多)但是从那以后早期版本的 Firefox 浏览器会在下载 HTML 时开始呈现 HTML,因此即使浏览器仍在等待响应完成,您也会看到一个页面。

【讨论】:

    【解决方案2】:

    这两行的顺序无关紧要,但如果client.set 先出现,则不调用res.send 意味着存在错误。如果async 函数发生错误,这可能会导致UnhandledPromiseRejectionWarning 警告将显示在控制台中。

    这个sn-p有几个问题。

    即使client.set 是异步的也会发生错误,这表明client.set 会导致未捕获的同步错误。

    client.set 没有被承诺,但它应该用于正确的控制流。它没有提供回调参数可能是导致错误的原因。

    正如this answer 中所述,Express 不支持承诺,所有拒绝都应明确处理以进行正确的错误处理。

    require 这样的所有通用代码都超出了中间件功能。应该是:

    const redis = require('redis')
    const redisURL = 'redis://127.0.0.1:6379';
    const  client = redis.createClient(redisURL);
    const util = require('util')
    client.get = util.promisify(client.get)
    client.set = util.promisify(client.set)
    
    app.get('/api/blogs', requireLogin, async (req, res, next) => {
      try {
        const cachedBlog = await client.get(req.user.id) 
    
        if (cachedBlog) {
          return res.send(JSON.parse(cachedBlogs))
        }
    
        const blogs = await Blog.find({_user: req.user.id});
        await client.set(req.user.id, JSON.stringify(blogs));
        res.send(blogs);
      } catch (err) {
        next(err);
      }
    })
    

    大多数流行的库都有允许跳过样板承诺代码的承诺对应物,这也适用于 redis

    【讨论】:

      【解决方案3】:

      这两个任务将异步运行,但执行顺序很重要。

      client.set(req.user.id, JSON.stringify(blogs)) 执行首先开始,但是由于你没有使用 await,promise 不会被解析但已经开始执行。 之后res.send() 将执行。

      您没有收到响应意味着在执行client.set(req.user.id, JSON.stringify(blogs)) 时出现了一些错误。

      使用 Try catch 块来跟踪此错误(如其他答案中所述)。

      您还可以在代码中添加这些行来捕获其他“unhandledRejection”或“uncaughtException”错误(如果有)。

      process.on('unhandledRejection', (err) => {
          logger.error('An unhandledRejection error occurred!');
          logger.error(err.stack)
      });
      process.on('uncaughtException', function (err) {
          logger.error('An uncaught error occurred!');
          logger.error(err.stack);
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-01-06
        • 2021-12-17
        • 1970-01-01
        • 2013-03-29
        • 1970-01-01
        • 2018-01-14
        • 1970-01-01
        相关资源
        最近更新 更多