Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/42.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在异步调用中嵌套异步调用是否可取?(Node.js)_Node.js_Asynchronous - Fatal编程技术网

在异步调用中嵌套异步调用是否可取?(Node.js)

在异步调用中嵌套异步调用是否可取?(Node.js),node.js,asynchronous,Node.js,Asynchronous,我正在玩Node.js,我创建了一个简单的脚本,可以将文件从目录上传到服务器: var request = require('request'); var file = require('file'); var fs = require('fs'); var path = require('path'); VERSION = '0.1' CONFIG_FILE = path.join(__dirname, 'etc', 'sender.conf.json'); var config =

我正在玩Node.js,我创建了一个简单的脚本,可以将文件从目录上传到服务器:

var request = require('request');
var file = require('file');
var fs = require('fs');
var path = require('path');


VERSION = '0.1'
CONFIG_FILE = path.join(__dirname, 'etc', 'sender.conf.json');


var config = JSON.parse(
  fs.readFileSync(CONFIG_FILE).toString()
);

var DATA_DIR = __dirname
config['data_dir'].forEach(function(dir) {
  DATA_DIR = path.join(DATA_DIR, dir)
});


console.log('sending data from root directory: ' + DATA_DIR);
file.walk(
  DATA_DIR,
  function(err, dir_path, dirs, files) {
    if(err) {
      return console.error(err);
    } 
    sendFiles(dir_path, files);
  } 
);

function sendFiles(dir_path, files)
{
  files
    .filter(function(file) {
      return file.substr(-5) === '.meta';
    })
    .forEach(function(file) {
      var name = path.basename(file.slice(0, -5));
      sendFile(dir_path, name);
    })
  ; 
} 

function sendFile(dir_path, name)
{
  console.log("reading file start: " + dir_path + "/" + name);
  fs.readFile(
    path.join(dir_path, name + '.meta'),
    function(err, raw_meta) {
      if(err) {
        return console.error(err);
      }
      console.log("reading file done: " + dir_path + "/" + name);
      sendData(
        name,
        JSON.parse(raw_meta),
        fs.createReadStream(path.join(dir_path, name + '.data'))
      );
    }
  );
  console.log("reading file async: " + dir_path + "/" + name);
}

function sendData(name, meta, data_stream)
{ 
  meta['source'] = config['data_source'];

  var req = request.post(
    config['sink_url'],
    function(err, res, body) {
      if(err) {
        console.log(err);
      }
      else {
        console.log(name);
        console.log(meta);
        console.log(body);
      }
    }
  );
  var form = req.form();

  form.append(
    'meta',
    JSON.stringify(meta),
    { 
      contentType: 'application/x-www-form-urlencoded'
    }
  );

  form.append(
    'data',
    data_stream
  );
}
当只运行几个文件时,它工作正常。但当我在包含大量文件的目录上运行它时,它会阻塞。这是因为它不断创建大量的任务来读取文件,但却从未真正执行读取(因为文件太多)。这可以在输出上观察到:

sending data from root directory: .../data
reading file start: .../data/ac/ad/acigisu-adruire-sabeveab-ozaniaru-fugeef-wemathu-lubesoraf-lojoepe
reading file async: .../data/ac/ad/acigisu-adruire-sabeveab-ozaniaru-fugeef-wemathu-lubesoraf-lojoepe
reading file start: .../data/ac/ab/acodug-abueba-alizacod-ugvut-nucom
reading file async: .../data/ac/ab/acodug-abueba-alizacod-ugvut-nucom
reading file start: .../data/ac/as/acigisu-asetufvub-liwi-ru-mitdawej-vekof
reading file async: .../data/ac/as/acigisu-asetufvub-liwi-ru-mitdawej-vekof
reading file start: .../data/ac/av/ace-avhad-bop-rujan-pehwopa
reading file async: .../data/ac/av/ace-avhad-bop-rujan-pehwopa
...
对于每个文件,在调用
fs.readFile
之前立即生成控制台输出
“reading file start”
,在安排异步读取之后立即生成
“reading file async”
。但是,即使我让它运行很长时间,也没有“reading file done”(读取文件已完成)消息,这意味着可能从来没有计划过读取任何文件(这些文件的大小约为100字节,因此一旦计划,这些读取可能会一次完成)

这让我想到了下面的思考过程。Node.js中的异步调用之所以完成,是因为事件循环本身是单线程的,我们不想阻止它但是,一旦满足了这一要求,将更多的异步调用嵌套到异步调用中是否有意义?这些异步调用本身嵌套在异步调用中,等等?是否有任何特殊用途?此外,如果单个文件的完整处理仅由同步调用组成,那么由于调度开销而导致的对代码的实际悲观情绪是否真的不需要,并且完全可以避免

鉴于上述思考过程,我的行动方针是使用以下解决方案:

  • 将所有文件的名称异步推送到
    async.queue
  • 通过设置
    queue.concurrency
  • 提供完全同步的文件上载处理程序,即同步读取文件内容,完成后,同步向服务器发送POST请求

这是我第一次尝试使用Node.js和/或JavaScript,因此很可能我完全错了(注意,例如,很明显,同步调用是不可取的,这与我上面的思考过程相矛盾——问题是为什么)。如对上述思维过程的有效性以及建议解决方案和最终替代方案的可行性提出任何意见,将不胜感激。

==Update=

在Node.js的文档中有非常详细的解释

至于手头的特定问题,实际上是在选择文件系统walker模块。解决方案是使用,例如,代替:

==原创帖子==

在进一步研究之后,我将尝试回答我自己的问题。这个答案仍然只是部分解决方案(非常感谢有实际Node.js经验的人提供更完整的答案)

对上述主要问题的简短回答是,确实不仅需要,而且几乎总是需要从已经异步的函数中调度更多的异步函数。详细的解释如下

这是因为Node.js调度的工作方式:。在链接的博客帖子下面的讨论中有两条非常重要的评论:

  • “Javascript总是先完成当前正在执行的函数。事件永远不会中断函数。”[]
  • “还要注意的是,它不仅会完成当前的函数,还会运行到所有同步函数的完成,我相信在处理请求回调之前,任何与process.nextTick一起排队的东西都会完成。”[]
在的文档中还提到了这一点:“在处理额外的I/O之前,事件循环的每次传递都会完全耗尽下一个勾号队列。因此,递归设置下一个勾号回调将阻止任何I/O的发生,就像一个while(true);循环一样。”

所以,总而言之,脚本本身的所有代码都运行在单线程和单线程上。计划运行的异步回调是在同一个线程上执行的,并且只有在当前的下一个勾号队列中的所有勾号都被清空之后才会执行。异步回调的使用提供了唯一的点,此时可以安排运行其他一些函数。如果文件上载处理程序不按照问题中所述计划任何其他异步任务,那么它的执行将阻止所有其他任务,直到整个文件上载处理程序完成为止。这是不可取的


这也解释了为什么输入文件的实际读取从未发生(“递归设置nextTick回调将阻止任何I/O的发生”-见上文)。它最终将在整个目录层次结构的所有任务都已被调度之后发生但是,如果没有进一步的研究,我无法回答如何限制计划的文件上载任务数量(任务队列的有效大小)并阻止计划循环,直到其中一些任务得到处理(任务队列上的一些空间已释放)。因此,这个答案仍然不完整。

==更新==

在Node.js的文档中有非常详细的解释

至于手头的特定问题,实际上是在选择文件系统walker模块。解决方案是使用,例如,代替:

==原创帖子==

在进一步研究之后,我将尝试回答我自己的问题。这个答案仍然只是部分解决方案(非常感谢有实际Node.js经验的人提供更完整的答案)

主要问题的简短回答
@@ -4,7 +4,7 @@


 var request = require('request');
-var file = require('file');
+var walk = require('walk');
 var fs = require('fs');
 var path = require('path');

@@ -24,13 +24,19 @@ config['data_dir'].forEach(function(dir) {


 console.log('sending data from root directory: ' + DATA_DIR);
-file.walk(
-  DATA_DIR,
-  function(err, dir_path, dirs, files) {
-    if(err) {
-      return console.error(err);
-    }
-    sendFiles(dir_path, files);
+var walker = walk.walk(DATA_DIR)
+walker.on(
+  'files',
+  function(dir_path, files, next) {
+    sendFiles(dir_path, files.map(function(stats) { return stats.name; }));
+    next();
+  }
+);
+walker.on(
+  'errors',
+  function(dir_path, node_stats, next) {
+    console.error('file walker:', node_stats);
+    next();
   }
 );