nodejs 表示 fs 将文件迭代到数组或对象中失败

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6287908/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 14:16:23  来源:igfitidea点击:

nodejs express fs iterating files into array or object failing

node.jsexpress

提问by thrice801

So Im trying to use the nodejs express FS module to iterate a directory in my app, store each filename in an array, which I can pass to my express view and iterate through the list, but Im struggling to do so. When I do a console.log within the files.forEach function loop, its printing the filename just fine, but as soon as I try to do anything such as:

所以我试图使用 nodejs express FS 模块来迭代我的应用程序中的一个目录,将每个文件名存储在一个数组中,我可以将它传递给我的快速视图并遍历列表,但我正在努力这样做。当我在 files.forEach 函数循环中执行 console.log 时,它可以很好地打印文件名,但是一旦我尝试执行以下操作,例如:

var myfiles = [];
var fs = require('fs');
fs.readdir('./myfiles/', function (err, files) { if (err) throw err;
  files.forEach( function (file) {
    myfiles.push(file);
  });
});
console.log(myfiles);

it fails, just logs an empty object. So Im not sure exactly what is going on, I think it has to do with callback functions, but if someone could walk me through what Im doing wrong, and why its not working, (and how to make it work), it would be much appreciated.

它失败了,只记录一个空对象。所以我不确定到底发生了什么,我认为它与回调函数有关,但是如果有人可以引导我了解我做错了什么,为什么它不起作用,(以及如何使它起作用),那就是非常感激。

回答by Rob Raisch

The myfiles array is empty because the callback hasn't been called before you call console.log().

myfiles 数组是空的,因为在调用 console.log() 之前没有调用回调。

You'll need to do something like:

您需要执行以下操作:

var fs = require('fs');
fs.readdir('./myfiles/',function(err,files){
    if(err) throw err;
    files.forEach(function(file){
        // do something with each file HERE!
    });
 });
 // because trying to do something with files here won't work because
 // the callback hasn't fired yet.

Remember, everything in node happens at the same time, in the sense that, unless you're doing your processing inside your callbacks, you cannot guarantee asynchronous functions have completed yet.

请记住,node 中的所有事情都是同时发生的,从某种意义上说,除非您在回调中进行处理,否则您不能保证异步函数已经完成。

One way around this problem for you would be to use an EventEmitter:

解决此问题的一种方法是使用 EventEmitter:

var fs=require('fs'),
    EventEmitter=require('events').EventEmitter,
    filesEE=new EventEmitter(),
    myfiles=[];

// this event will be called when all files have been added to myfiles
filesEE.on('files_ready',function(){
  console.dir(myfiles);
});

// read all files from current directory
fs.readdir('.',function(err,files){
  if(err) throw err;
  files.forEach(function(file){
    myfiles.push(file);
  });
  filesEE.emit('files_ready'); // trigger files_ready event
});

回答by David Tang

fs.readdiris asynchronous (as with many operations in node.js). This means that the console.logline is going to run before readdirhas a chance to call the function passed to it.

fs.readdir是异步的(就像 node.js 中的许多操作一样)。这意味着该console.log行将在readdir有机会调用传递给它的函数之前运行。

You need to either:

您需要:

Put the console.logline within the callback function given to readdir, i.e:

将该console.log行放在给定的回调函数中readdir,即:

fs.readdir('./myfiles/', function (err, files) { if (err) throw err;
  files.forEach( function (file) {
    myfiles.push(file);
  });
  console.log(myfiles);
});

Or simply perform some action with each file inside the forEach.

或者简单地对forEach.

回答by Automatico

As several have mentioned, you are using an async method, so you have a nondeterministic execution path.

正如一些人提到的,您使用的是异步方法,因此您有一个不确定的执行路径。

However, there is an easy way around this. Simply use the Sync version of the method:

但是,有一个简单的方法可以解决这个问题。只需使用该方法的同步版本:

var myfiles = [];
var fs = require('fs');

var arrayOfFiles = fs.readdirSync('./myfiles/');

//Yes, the following is not super-smart, but you might want to process the files. This is how:
arrayOfFiles.forEach( function (file) {
    myfiles.push(file);
});
console.log(myfiles);

That should work as you want. However, using sync statements is not good, so you should not do it unless it is vitally important for it to be sync.

这应该可以正常工作。但是,使用同步语句并不好,因此除非同步非常重要,否则您不应该这样做。

Read more here: fs.readdirSync

在此处阅读更多信息:fs.readdirSync

回答by Karl Knechtel

I think it has to do with callback functions,

我认为这与回调函数有关,

Exactly.

确切地。

fs.readdirmakes an asynchronous request to the file system for that information, and calls the callback at some later time with the results.

fs.readdir向文件系统发出异步请求以获取该信息,并在稍后使用结果调用回调。

So function (err, files) { ... }doesn't run immediately, but console.log(myfiles)does.

所以function (err, files) { ... }不会立即运行,但console.log(myfiles)会运行。

At some later point in time, myfileswill contain the desired information.

在稍后的某个时间点,myfiles将包含所需的信息。

You should note BTW that filesis already an Array, so there is really no point in manually appending each element to some other blank array. If the idea is to put together the results from several calls, then use .concat; if you just want to get the data once, then you can just assign myfiles = filesdirectly.

您应该注意 BTWfiles已经是一个数组,因此手动将每个元素附加到其他空白数组实际上没有意义。如果想法是将多个调用的结果放在一起,则使用.concat; 如果你只想获取一次数据,那么你可以直接赋值myfiles = files

Overall, you really ought to read up on "Continuation-passing style".

总的来说,你真的应该阅读“Continuation-passing style”

回答by kochod

I faced the same problem, and basing on answers given in this post I've solved it with Promises, that seem to be of perfect use in this situation:

我遇到了同样的问题,根据这篇文章中给出的答案,我已经用Promises解决了这个问题,这在这种情况下似乎非常有用:

router.get('/', (req, res) => {
  var viewBag = {}; // It's just my little habit from .NET MVC ;)

  var readFiles = new Promise((resolve, reject) => {
    fs.readdir('./myfiles/',(err,files) => {
      if(err) { 
        reject(err); 
      } else {
        resolve(files);
      }
    });
  });

  // showcase just in case you will need to implement more async operations before route will response
  var anotherPromise = new Promise((resolve, reject) => {
    doAsyncStuff((err, anotherResult) => {
      if(err) { 
        reject(err); 
      } else {
        resolve(anotherResult);
      }
    });
  });

  Promise.all([readFiles, anotherPromise]).then((values) => {
    viewBag.files = values[0];
    viewBag.otherStuff = values[1];
    console.log(viewBag.files); // logs e.g. [ 'file.txt' ]
    res.render('your_view', viewBag);
  }).catch((errors) => {
    res.render('your_view',{errors:errors}); // you can use 'errors' property to render errors in view or implement different error handling schema
  });
});

Note:you don't have to push found files into new array because you already get an array from fs.readdir()'c callback. According to node docs:

注意:您不必将找到的文件推送到新数组中,因为您已经从 fs.readdir()'c 回调中获得了一个数组。根据节点文档

The callback gets two arguments (err, files) where filesis an arrayof the names of the files in the directory excluding '.' and '..'.

回调获取两个参数(err、files),其中files目录中文件名称的数组,不包括 '.' 和 '..'。

I belive this is very elegant and handy solution, and most of all - it doesn't require you to bring in and handle new modules to your script.

我相信这是一个非常优雅和方便的解决方案,最重要的是 - 它不需要您为脚本引入和处理新模块。