Node JS Express处理多个请求

8
我的情况: 我有一个基于node.js express框架的服务端get接口,该接口会调用其他API并且这些API耗时较长(大约2秒左右)。我通过回调函数来调用此函数,并触发res.send作为回调函数的一部分。res.send对象封装了一个在从这些耗时API调用中收到结果后创建的对象。因此,只有当我从API调用中获取到所有信息时,才能发送res.send。
以下是一些代表性的代码。
someFunctionCall(params, callback)
{
  // do some asyncronous requests.
  Promise.all([requestAsync1(params),requestAsync2(params)]).then
  {
     // do some operations
     callback(response) // callback given with some data from the promise
  }

}
app.get('/',function(req, res){
 someFunctionCall(params, function(err, data){
    res.send(JSON.stringify(data))
   }
}
我想要的 我希望我的服务器能够处理其他并行的get请求,而不会因为其他函数中的REST API调用而被阻塞。但问题在于,只有当承诺得到实现时,回调才会被发出,这些操作都是异步的,但我的线程将等待所有操作的执行。而且Node在没有执行前一个请求的res.send或res.end的情况下不接受下一个get请求。当我有多个请求进来时,每一个请求都会一个接一个地执行,这就成为一个问题。

注意:我不想使用集群方法,我只想知道是否可以在没有集群的情况下做到这一点。


1
如果你在耗时操作中使用了所有的异步I/O(这是你应该做的),那么你对node.js的工作原理的理解是不正确的。Node.js应该能够在等待耗时API的同时处理许多其他请求。Promise和异步操作不会阻止node.js处理其他请求。 - jfriend00
@jfriend00,我并不是在说promises会阻塞,我只是在说回调函数只有在所有promises都解决后才会发生,这从技术上讲使它成为一个阻塞调用,不是吗?不是这样吗? - Kakarot
不是的。这并不会阻塞其他事件的运行。这就是在node.js中异步操作的工作方式。当node.js等待回调被调用时,其他事情仍然可以正常运行。它的工作方式与setTimeout(callback, t)完全相同。在等待回调的同时,node.js中的其他事情也可以正常运行。 - jfriend00
请注意,您展示的 .then() 处理程序的 Javascript 语法完全不正确。应该是 Promise.all([requestAsync1(params),requestAsync2(params)]).then(function(response) {callback(response)}); - jfriend00
1个回答

16

你显然误解了node.js、异步操作和promise的工作方式。假设你的长时间运行的异步操作都是正确使用异步I/O编写的,那么你的requestAsync1(params)requestAsync2(params)调用都不会阻塞。这意味着当你等待Promise.all()调用其.then()处理程序来表示这两个异步操作完成时,node.js可以完全自由地运行其他事件或传入请求。Promises本身并不阻塞,因此node.js事件系统可以处理其他事件。所以,你要么根本没有阻塞问题,要么如果你确实有,那么它不是由你在这里询问的原因引起的。

为了查看你的代码是否真正阻塞,你可以临时添加一个像这样输出到控制台的简单计时器:

let startTime;
setInterval(function() {
    if (!startTime) {
        startTime = Date.now();
    }
    console.log((Date.now() - startTime) / 1000);
}, 100)

当事件循环未被阻塞时,每100毫秒输出一个简单的相对时间戳。显然,在生产代码中你不会将其保留,但它可以帮助你判断事件循环是否被阻塞。


我在你问题所包含的代码中看到了一个奇怪的语法问题。这段代码:

someFunctionCall(params, callback)
{
  // do some asyncronous requests.
  Promise.all([requestAsync1(params),requestAsync2(params)]).then
  {
     // do some operations
     callback(response) // callback given with some data from the promise
  }

}

应该像这样表达:

someFunctionCall(params, callback)
{
  // do some asyncronous requests.
  Promise.all([requestAsync1(params),requestAsync2(params)]).then(function(response)
  {
     // do some operations
     callback(response) // callback given with some data from the promise
  }

});

但是,更好的设计是只返回 Promise 而不切换回普通回调。除了允许调用者使用更灵活的 Promise 方案外,还可以避免在任何一个异步操作中出现错误。建议使用以下代码:

someFunctionCall(params) {
  // do some asyncronous requests.
    return Promise.all([requestAsync1(params),requestAsync2(params)]).then(function(results) {
        // further processing of results
        // return final resolved value of the promise
        return someValue;
    });
}

然后,调用者会像这样使用:

someFunctionCall(params).then(function(result) {
    // process final result here
}).catch(function(err) {
    // handle error here
});

检查事件循环中是否有任何同步操作的建议非常好,我的确有一个同步操作在第一次查阅代码时被我忽视了。非常感谢您的帮助。至于您提出的第二个问题,有关then()语法的问题,我使用了正确的语法,但在编写类似代码时犯了错误。 - Kakarot
如果在 Promise 对象上使用 await,它不会阻塞后续请求吗?@Kakarot - Sasi Kumar
1
@SasiKumar - 我不确定你在问哪段代码。await 暂停当前函数的执行,直到你等待的 Promise 完成,但是在 await 期间事件循环中的其他操作仍然可以正常运行。 - jfriend00

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接