【问题标题】:How to truly make a time consuming request async in node如何在节点中真正使耗时的请求异步
【发布时间】:2019-02-12 15:29:36
【问题描述】:

我正在尝试扩展我的 nodejs 应用程序,以便它可以同时有效地处理多个请求。实现这一点的一种方法是通过代码优化,我想知道如何使耗时的请求异步,承诺是唯一的方法吗?还是有其他机制可以达到同样的效果?

我已经将 Promise 用于异步任务,但据我了解 Promise,真正的异步部分是 then 和 catch 处理程序。当创建一个 promise 时,executor 函数将由主线程执行,直到它执行一些异步任务(setTimeout 等)。

发送邮件示例代码

   app.route('/api/contact/sendEmail')
       .post((req, res) =>{
           sendEmail(req).then( () =>{
               res.status(200);
               res.json({
                   sent: true
               });
           }).catch(emailError=> {
               //Custom error send bad request.
               if(emailError.code === 900) {
                   res.status(400);
                   res.json({
                       errorCode: emailError.code,
                       message: emailError.message
                   });
               }else {
                   res.status(500);
                   res.json({
                       errorCode: emailError.code
                   });
               }
           });
       });

线程不会被阻塞来发送响应,但是直到 sendEmail 没有到达实际的异步部分,主线程才会被阻塞

【问题讨论】:

    标签: node.js express


    【解决方案1】:

    回答您的问题有点困难,因为您没有提供有关您正在使用的库/函数的详细信息,例如sendMail 函数来自哪里,app 是什么,以及您认为什么时候出现了阻塞。

    但是,一般来说,您在 Node.JS 中的代码总是在主线程中执行。这意味着,您的 .js 文件的每一行都在主线程中,无论是否有“异步”。

    现在,要真正实现“异步”(即:在执行其他代码的同时执行),必须有一种方法可以将内容推入另一个线程执行。这就是 Node.JS 中实际发生的事情。 Node.JS 使用libuv / libev 来实现一个事件循环来处理任务。它还具有许多后台/工作线程(默认为 4 个),它们执行阻塞任务,例如网络或文件 I/O。

    然而,这是从 Node.JS 开发人员中抽象出来的,因为它隐藏在相应的 模块 的实现中。例如,内置文件系统或网络模块将在后台线程中执行它们的任务(除非您使用-sync 函数变体之一)。 如果您使用的是第三方模块,则取决于它是否用 C/C++ 编写并使用 libuv 访问工作线程并进行后台处理。如果不是,那么您的第三方模块将在与您的代码相同的主线程上执行代码,如果是,那么执行实际上是在后台线程中“异步”完成的。

    另请参阅这个内容丰富的帖子:How the single threaded non blocking IO model works in Node.js

    这对您有什么帮助?
    基本上我想告诉你,这一切都取决于你正在使用的功能/模块,以及它们是否被实现为后台操作(使用libuv)。最重要的是,你不能自己做任何异步/同步,你必须使用函数/模块实现的任何东西。

    为了完整起见,我想提一下 Node.JS 11 引入了 worker_threads 模块,它允许在 javascript 代码中使用线程,从而允许您将某些内容放入后台线程而不阻塞主线程。请注意,模块的稳定性仍然是“实验性的”。

    旁注
    你确定你的代码阻塞了主线程吗?因为如前所述,网络IO无论如何都发生在后台线程中,所以sendMail不太可能阻塞主线程(假设sendMail的工作是做网络IO)。 你是怎么发现它被阻塞的?

    【讨论】:

    • 好吧,如果我必须通过我的代码做一些非阻塞的事情,并且如果我使用的模块或函数不使用后台线程,那么我最好的选择是只坚持承诺? app=express() 作为旁注,我错过了添加 sendEmail 使用 nodemailer。因此,我假设发送电子邮件的实际部分是非阻塞的,但在控制到达电子邮件的实际发送之前,我的主线程将保持阻塞。感谢您提供有关 libuv/libve 的详细帖子,将查看您分享的文章以及实验模块的文档。
    • Promise 只是提供了不同的 API 来做同样的事情。异步/非异步没有区别。它只允许您以bla.then().then().then().catch() 等风格编写代码,而不是bla(function callback(...) { moreStuff(function anotherCallback(...) { } }); });async/await 关键字也是如此。
    • 关于您的观点“但在控制到达实际发送电子邮件之前,我的主线程将保持阻塞”,我不太确定我是否理解您。您发布的代码将一个路由器附加到您的app,并将一个处理程序附加到该处理程序的 HTTP POST 请求。每次收到 HTTP 请求时都会调用此处理程序(带有签名 (req,res) => () 的函数)。没有部分可以用“直到控制到达实际发送”来引用,因为处理程序中的第一个表达式是sendMail
    • 换句话说:app.route().post() 不会阻塞,它会设置你的路由,然后返回。您提供的回调函数 (req,res) => () 会在每个请求到达您的应用程序后被调用。您在哪里看到实际的阻塞
    • 据我了解,只要 sendEmail 没有真正开始执行 IO,主线程就会被阻塞。所以 post 路由不会阻塞,但是你提到的调用 sendEmail 调用 nodemailer 发送 API 的回调的实际执行将被阻塞,直到 nodemailer 返回一个承诺(一旦它开始异步电子邮件发送就返回)。 Promise 将通过将 then 和 catch 处理程序添加到队列的末尾来延迟它们,这与回调会在调用后立即开始执行并因此阻塞,对吧?
    猜你喜欢
    • 2018-02-11
    • 1970-01-01
    • 1970-01-01
    • 2014-08-23
    • 2016-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多