我对以下代码有疑问(来源: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);
我有这些问题:
superagent.get(
wttr.in / $ {city} )
向http://wttr.in/sf
发出网络请求时,该请求将被放置在任务队列中主调用堆栈不正确吗?
superagent.get(
wttr.in / $ {city} ).end(...)
(将被推送到任务队列)在主调用堆栈为空正确之前不会被调用?换句话说,在事件循环的每个刻度上,它将从任务队列中获取一个项目?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请求的堆栈会先运行吗?答案 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轮询阶段时,使用计时器函数遵循的路径相同,则将执行带有抓取操作的函数。