【问题标题】:NodeJS Event Loop Multiple RequestNodeJS 事件循环多个请求
【发布时间】:2017-06-28 06:49:40
【问题描述】:

我对以下代码有疑问(来源:https://blog.risingstack.com/node-js-at-scale-understanding-node-js-event-loop/):

'use strict'  
const express = require('express')  
const superagent = require('superagent')  
const app = express()

app.get('/', sendWeatherOfRandomCity)

function sendWeatherOfRandomCity (request, response) {  
  getWeatherOfRandomCity(request, response)
  sayHi()
}

const CITIES = [  
  'london',
  'newyork',
  'paris',
  'budapest',
  'warsaw',
  'rome',
  'madrid',
  'moscow',
  'beijing',
  'capetown',
]

function getWeatherOfRandomCity (request, response) {  
  const city = CITIES[Math.floor(Math.random() * CITIES.length)]
  superagent.get(`wttr.in/${city}`)
    .end((err, res) => {
      if (err) {
        console.log('O snap')
        return response.status(500).send('There was an error getting the weather, try looking out the window')
      }
      const responseText = res.text
      response.send(responseText)
      console.log('Got the weather')
    })

  console.log('Fetching the weather, please be patient)
}

function sayHi () {  
  console.log('Hi')
}

app.listen(3000);

我有这些问题:

  1. 例如,当 getWeatherOfRandomCity 方法中的 superagent.get(wttr.in/${city})http://wttr.in/sf 发出 Web 请求时,该请求将被放置在任务队列中,而不是正确的主调用堆栈?
  2. 如果主调用堆栈上有方法(即主调用堆栈不为空),则附加到superagent.get(wttr.in/${city}).end(...) 的结束事件(将被推送到任务队列)在主调用堆栈为空之前不会被调用,对吗?换句话说,在事件循环的每个滴答声中,它都会从任务队列中取出一项?
  3. 假设两个对localhost:3000/ 的请求一个接一个地进来。第一个请求将 sendWeatherOfRandomCity 入栈,getWeatherOfRandomCity 入栈,然后 web 请求 superagent.get(wttr.in/${city}).end(...) 将被放入后台队列,然后console.log('Fetching the weather, please be patient'),然后sendWeatherOfRandomCity 将从堆栈中弹出,最后sayHi() 将被压入堆栈,它将打印“Hi”并从堆栈中弹出,最后,附加到superagent.get(wttr.in/${city}@ 的结束事件987654334@ 将从任务队列中调用,因为主调用堆栈将为空。现在,当第二个请求到来时,它会将与第一个请求相同的所有内容推送到主调用堆栈上,但第一个请求(仍在任务队列中)的结束处理程序将首先运行,或者推送到主调用的内容第二个网络请求的堆栈将首先运行?

【问题讨论】:

  • 您的任务队列概念有缺陷。当您调用异步操作时,它们会立即启动——它们不会进入任务队列。结果完成后将在事件队列中排队,并按照它们排队的顺序(它们完成的顺序,从而触发回调)提供服务。仅当解释器未运行任何其他 Javascript(它是单线程的)时,才会从事件队列中提取事件(这将导致调用回调)。
  • 另外传入事件不会以您描述的方式推送到堆栈中。有一个单独的事件队列与堆栈没有连接。当 node.js 运行完所有其他 Javascript 后,它会从事件队列(使用空堆栈)中提取下一个事件并调用与之关联的回调。这会触发该回调中的一些 Javascript 运行。该回调可能以前在函数闭包中保存了状态(它们是被垃圾收集的独立对象,而不是在堆栈上)。
  • 但是假设异步操作完成并且它的结果和回调被放置在事件队列中但是主调用堆栈中仍然有代码。那么在主调用栈为空之前不会调用事件队列中的回调对吗?
  • 正确。 node.js 是单线程的,一次一件事。它运行当前的 JS 片段,直到将控制权返回给系统,此时它从事件队列中提取下一个事件并运行与之关联的回调。每个回调都从一个空堆栈开始。这就是 nodejs 描述“事件驱动”的意思。事件发生,运行与该事件关联的一些 Javascript,完成该 Javascript,查看事件队列中是否有另一个事件并运行它等等。
  • 只有一个 Javascript 堆栈,每次调用事件队列的回调时,堆栈都是空的。所以我不知道你的问题是什么意思,因为这意味着可能有不止一个堆栈。只有一个。

标签: javascript node.js events event-loop


【解决方案1】:

当您发出 http 请求时,libuv 会看到您正在尝试发出网络请求。 libuv 和 node 都没有任何代码来处理与网络请求相关的所有这些操作。相反,libuv 将请求委托给底层操作系统。

实际上是内核,它是我们操作系统的基本部分,它执行真正的网络请求工作。 Libuv 用于发出请求,然后它只是等待操作系统发出信号,表明某些响应已返回到请求。因此,由于 Libuv 将完成的工作委托给操作系统,操作系统本身决定是否制造新的威胁。或者只是一般如何处理发出请求的整个过程。每个不同的操作系统都有不同的处理方法:在linux上是epoll,在mac os中称为kqueue,在windows中称为GetQueuedCompletionStatusEx。

事件循环有 6 个阶段,其中之一是 i/o poll。每个阶段都优先于其他阶段。数字 1 始终是计时器。当时间到了,(或事件完成)定时器的回调将被调用到事件队列,定时器函数也将移动到事件队列。然后事件循环将检查它的调用堆栈是否可用。调用堆栈是函数执行的地方。您一次可以做一件事,调用堆栈强制我们只能在调用堆栈的顶部拥有一个函数,这就是我们正在做的事情。在 JAVASCRIPT RUNTIME 中无法同时执行两件事.

如果调用堆栈为空意味着 main() 函数被删除,事件循环将定时器函数推送到调用堆栈并执行您的函数。在主函数完成之前,任何异步回调都不会运行。

因此,当处理传入数据和连接的 i/o 轮询阶段时,使用与计时器函数遵循的相同路径,将执行处理获取的函数。

【讨论】:

    猜你喜欢
    • 2013-11-23
    • 2020-11-22
    • 1970-01-01
    • 1970-01-01
    • 2012-05-27
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    相关资源
    最近更新 更多