NodeJS事件循环多个请求

时间:2017-02-10 02:43:18

标签: javascript node.js event-loop

我对以下代码有疑问(来源: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发出网络请求时,该请求将被放置在任务队列中主调用堆栈不正确吗?
    1. 如果主调用堆栈上有方法(即主调用堆栈不为空),则结束事件附加到superagent.get( wttr.in / $ {city} ).end(...) (将被推送到任务队列)在主调用堆栈为空正确之前不会被调用?换句话说,在事件循环的每个刻度上,它将从任务队列中获取一个项目?
    2. 让我们说两个localhost:3000/请求一个接一个地进来。第一个请求将推送堆栈上的sendWeatherOfRandomCity,堆栈上的getWeatherOfRandomCity,然后web请求superagent.get( wttr.in / $ {city} ).end(...)将被放置在后台队列中,然后{{ 1}},然后sendWeatherOfRandomCity将从堆栈弹出,最后console.log('Fetching the weather, please be patient')将被推入堆栈,它将打印"嗨"并弹出堆栈,最后将从任务队列中调用附加到sayHi() wttr.in / $ {city} superagent.get(的结束事件,因为主调用堆栈将为空。现在,当第二个请求到来时,它会将所有与第一个请求相同的内容推送到主调用堆栈,但是第一个请求(仍然在任务队列中)的结束处理程序将首先运行,或者将内容推送到主调用第二个Web请求的堆栈会先运行吗?

1 个答案:

答案 0 :(得分:1)

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

实际上,内核是我们操作系统中执行真正的网络请求工作的必要部分。使用Libuv发出请求,然后仅等待操作系统发出信号,表明某些响应已返回到请求。因此,由于Libuv将工作委托给操作系统,因此操作系统本身将决定是否提出新的威胁。或者只是通常如何处理发出请求的整个过程。每个不同的操作系统都有不同的方法来处理此问题:在Linux上,它是epoll;在Mac os中,它称为kqueue;在Windows中,它称为GetQueuedCompletionStatusEx。

事件循环中有6个阶段,其中之一是I / O轮询。每个阶段都优先于其他阶段。 1号始终是计时器。时间到时(或事件完成),计时器的回调将被调用到事件队列中,计时器功能也将移到事件队列中。然后事件循环将检查其调用堆栈是否可用。调用堆栈是函数执行的地方。您一次只能做一件事,并且调用栈强制我们只能在我们正在做的事情的顶部拥有一个函数。在JAVASCRIPT RUNTIME中无法同时执行两件事

如果调用堆栈为空,则意味着main()函数已删除,事件循环将把计时器函数推入调用堆栈,然后将执行您的函数。在完成主函数之前,绝不会运行任何异步回调。

因此,当需要处理输入数据和连接的I / O轮询阶段时,使用计时器函数遵循的路径相同,则将执行带有抓取操作的函数。