是什么导致node.js等待请求完成?

时间:2016-02-01 03:48:20

标签: javascript node.js

所以我浪费了大量时间编写这样的代码:

function processResponse(error, response, body) {
    if (!error && response.statusCode == 200) {
        console.log(body);
    } else {
        console.error(util.inspect(response, false, null));
    }
    waiting = false;
};

request.get(requestOpts.url, processResponse);

console.log("Waiting");
while(waiting) {
    count += 1;
    if(count % 10000000 == 0) {
        console.log(count);
    }
}

我试图让节点等待(而不是退出),直到响应从网络服务器返回。事实证明,这不起作用,工作无所作为。只是:

request.get(requestOpts.url, processResponse);

请求如何在回调挂起时保持节点退出?

3 个答案:

答案 0 :(得分:12)

节点始终跟踪任何待处理的回调,并且在达到零之前不会退出。这将包括所有活动的网络连接/请求以及文件系统IO和子进程活动。您需要编码才能获得预期的行为。默认情况下,节点将执行您期望的操作。

答案 1 :(得分:9)

<强> TLDR

在OP的代码中,同步while循环阻止事件循环到达轮询阶段,因此事件循环卡在其第一个滴答中并且网络I / O是从未处理过。

完整答案

Node.js的异步编程模型基于一个名为事件循环的同步循环。事件循环的基本抽象是函数调度:Node.js中的函数能够调度其他函数(如网络请求处理程序和计时器)以在将来的某个时刻运行 / em>和以响应某些事件

事件循环基本上是一个连续运行的“while循环”。在循环的每个“tick”期间,Node.js运行时检查是否满足调度函数的条件 - 如果满足条件(如果已经过了计时器),则执行该函数。

事件循环处理particular order中的回调。

   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘

处理网络请求的唯一地方是投票阶段;因此,为了通过回调处理网络请求,事件循环必须达到轮询阶段

事件循环的每个阶段只能在前一阶段的同步函数完成运行之后才会前进 - 即它们已经返回,并且调用堆栈为空。

在OP的代码中,同步while循环阻止事件循环到达轮询阶段,因此永远不会执行网络请求处理程序。

在等待网络请求时循环播放

为了在我们等待网络请求时在循环中运行代码,我们需要以某种方式为事件循环提供处理其他回调的机会。

我们可以通过在setTimeout()计划的回调中运行循环来实现这一目标。 setTimeout()是一种机制,用于调度在事件循环的timers阶段运行的函数。

每次循环结束时,事件循环都有机会处理新的I / O事件。如果没有新的I / O事件,它将转到通过setTimeout()安排的下一个loop()处理程序。

const request = require('request')

let count = 0
let isFinished = false

request.get('https://www.google.com', (err, res) => (isFinished = true))

console.log('Waiting')
loop()

function loop () {
  setTimeout(
    () => {
      count += 1
      if (isFinished) {
        console.log('done')
        return
      }

      if(count % 10 == 0) {
          console.log(count);
      }

      return loop()
    },
    0
  )
}

在此示例中,我们保持“递归”调用loop,直到网络请求完成。每次setTimeout()处理程序(由loop()调度)返回时,事件循环都会超出timers阶段并检查新的网络I / O(即我们对Google的请求)。响应准备就绪后,我们的网络请求处理程序将在poll阶段调用。

即使loop()是“递归的”,它也不会增加调用堆栈的大小。由于每个循环迭代都是通过setTimeout调度的,因此loop()函数不会将另一个loop()调用推送到调用堆栈,直到setTimeout()处理程序在下一个timers中运行1}}阶段 - 此时前一个loop()调用的调用堆栈已被清除。

答案 2 :(得分:2)

此代码应该足够了:

function processResponse(error, response, body) {
    console.log("Request complete");
    if (!error && response.statusCode == 200) {
        console.log(body);
    } else {
        console.error(util.inspect(response, false, null));
    }
};

console.log("Sending a request");
// Just as a test, try passing the URL hardcoded here,
// because your problem might be just a invalid requestOpts object
request.get("http://stackoverflow.com", processResponse);

您之前的代码完全错误的一件事是循环实际上阻止了请求的执行。 Node.js有一个单独的事件循环线程,该代码只消耗100%的CPU,没有为您的请求提供空间。

但是,上面的代码不能按预期工作,它可能与您正在使用的请求库有关。如果您使用的是request-promise而不是request,则需要使用不同的语法。