了解两个异步代码段的区别

时间:2018-11-13 20:41:12

标签: node.js asynchronous redis

我试图弄清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中,我正在考虑将它们都异步运行,因此我认为顺序无关紧要。

谁能告诉我我想念或无法理解的内容?

3 个答案:

答案 0 :(得分:1)

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

此代码段存在几个问题。

即使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

答案 1 :(得分:1)

由于OP要求了解两者之间的差异,因此请不要修改代码:

express运行请求处理程序功能并捕获同步错误(它们变为http500错误)。它对异步函数返回的承诺不做任何事情,并且在内部不使用await,因此您不会免费获得异步函数的错误处理。所有异步错误都必须被捕获并传递到next回调中,或者通过发送适当的状态代码和错误正文在代码中进行处理。

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

另一种方法-您在错误之前发送响应,这样您就可以得到页面,但是响应不会结束(我假设连接保持打开状态,就像后端将发送更多内容一样),但是从那以后早期版本的Firefox浏览器会在下载HTML时开始呈现HTML,因此即使浏览器仍在等待响应完成,您也会看到一个页面。

答案 2 :(得分:1)

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

client.set(req.user.id, JSON.stringify(blogs))首先开始执行,但是由于您不使用等待,承诺不会被解决,但是执行已经开始。 之后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);
});