Node.js事件循环对我没有意义

时间:2014-07-27 20:56:17

标签: javascript node.js event-loop

我是Node.js的新手。我一直在努力通过" Node.js正确的方式"作者:吉姆·威尔逊和我在书中遇到的矛盾(以及在Node.js本身?),我已经无法通过任何数量的谷歌搜索来调和我的满意度。

我已经在书中和我在网上看到的其他资源中反复说过,Node.js会逐行响应一些事件运行回调直到完成,然后事件循环继续等待或调用下一个回调。而且因为Node.js是单线程的(并且没有明确地对集群模块做任何事情,也作为单个进程运行),我的理解是,最多只有一个块一次执行的JavaScript代码。

我理解正确吗?这就是矛盾(在我看来)。如果是这种情况,Node.js如何高度并发?

这是一本直​​接来自本书的例子,说明了我的困惑。它旨在遍历数千个XML文件的目录,并将每个文件的相关位提取到JSON文档中。

首先是解析器:

'use strict';
const
  fs = require('fs'),
  cheerio = require('cheerio');

module.exports = function(filename, callback) {
  fs.readFile(filename, function(err, data){
    if (err) { return callback(err); }
    let
      $ = cheerio.load(data.toString()),
      collect = function(index, elem) {
        return $(elem).text();
      };

    callback(null, {
      _id: $('pgterms\\:ebook').attr('rdf:about').replace('ebooks/', ''), 
      title: $('dcterms\\:title').text(), 
      authors: $('pgterms\\:agent pgterms\\:name').map(collect), 
      subjects: $('[rdf\\:resource$="/LCSH"] ~ rdf\\:value').map(collect) 
    });
  });
};

走遍目录结构的那一步:

'use strict';
const

  file = require('file'),
  rdfParser = require('./lib/rdf-parser.js');

console.log('beginning directory walk');

file.walk(__dirname + '/cache', function(err, dirPath, dirs, files){
  files.forEach(function(path){
    rdfParser(path, function(err, doc) {
      if (err) {
        throw err;
      } else {
        console.log(doc);
      }
    });
  });
});

如果运行此代码,则会因程序耗尽所有可用文件描述符而导致错误。这似乎表明该程序同时打开了数千个文件。

我的问题是......除非事件模型和/或并发模型的行为与解释它们的方式不同,否则这怎么可能呢?

我确定那里有人知道这一点,并且可以解释它,但是目前,让我很困惑!

3 个答案:

答案 0 :(得分:4)

  

我理解正确吗?

  

如果是这种情况,Node.js如何高度并发?

不是javascript执行本身是并发的 - IO(和其他繁重的任务)是。当您调用异步函数时,它将启动任务(例如,读取文件)并立即返回“运行脚本的下一行”。然而,任务将在后台(同时)继续读取文件,一旦完成,它将把已分配给它的回调放到事件循环队列中,该队列将使用当时可用的数据调用它。

有关此“在后台”处理的详细信息,以及节点如何实际设法并行运行所有这些异步任务,请查看问题Nodejs Event Loop

答案 1 :(得分:2)

这是一个非常简单的描述,并且跳过很多东西。

files.forEach并非荒谬。因此,代码遍历目录中的文件列表,在每个文件上调用fs.readFile,然后返回到事件循环。

然后循环有一大堆要处理的文件打开事件,然后排队文件读取事件。然后循环可以开始通过并使用已读取的数据调用回调到fs.readFile。这些只能一次调用一个:正如你所说,任何时候只有一个线程执行javascript。

但是,在调用任何这些回调之前,您已经打开了该原始列表中的每个文件,如果有太多文件,则会导致文件句柄耗尽。

答案 2 :(得分:0)

我认为OrangeDog的答案是对您具体问题的正确答案。但也许你会发现Philip this简短而精彩的演示文稿,Philip Roberts很有帮助,这解释了事件循环的概念和JavaScript的异步处理。请注意,视频与node.js不相关,因为这些原则适用于所有JavaScript代码。