nodejs выражают fs итерацию файлов в массив или объект, потерпевший неудачу - PullRequest
6 голосов
/ 09 июня 2011

Итак, я пытаюсь использовать модуль nodejs Express FS, чтобы выполнить итерацию каталога в моем приложении, сохранить каждое имя файла в массиве, который я могу передать в свое представление Express и перебрать в списке, но я изо всех сил пытаюсь это сделать.Когда я делаю console.log в функциональном цикле files.forEach, он прекрасно печатает имя файла, но как только я пытаюсь сделать что-то, например:

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);

, это не удается, просто заносится в журнал пустой файл.объект.Так что я не уверен, что именно происходит, я думаю, что это связано с функциями обратного вызова, но если бы кто-то мог объяснить мне, что я делаю неправильно, и почему это не работает (и как заставить это работать), это было бывысоко ценится.

Ответы [ 5 ]

31 голосов
/ 09 июня 2011

Массив myfiles пуст, потому что обратный вызов не был вызван до вызова console.log ().

Вам нужно сделать что-то вроде:

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.

Помнитевсе в узле происходит одновременно, в том смысле, что, если вы не выполняете свою обработку внутри обратных вызовов, вы не можете гарантировать, что асинхронные функции еще не завершены.

Один из способов решения этой проблемы для вас -использовать 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
});
5 голосов
/ 09 июня 2011

fs.readdir является асинхронным (как и во многих операциях в node.js). Это означает, что строка console.log будет запущена до того, как readdir сможет вызвать переданную ей функцию.

Вам нужно либо:

Поместите строку console.log в функцию обратного вызова, заданную для readdir, т.е.:

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

Или просто выполните какое-то действие с каждым файлом внутри forEach.

4 голосов
/ 21 апреля 2013

Как уже упоминалось, вы используете асинхронный метод, поэтому у вас есть недетерминированный путь выполнения.

Однако есть простой способ обойти это.Просто используйте синхронизированную версию метода:

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);

Это должно работать так, как вы хотите.Однако использовать операторы синхронизации нехорошо, поэтому вам не следует делать это, если для синхронизации не жизненно важно.

Подробнее читайте здесь: fs.readdirSync

2 голосов
/ 09 июня 2011

Я думаю, что это связано с функциями обратного вызова,

Точно.

fs.readdir делает асинхронный запрос к файловой системе для этой информации и вызываетобратный вызов через некоторое время с результатами.

То есть function (err, files) { ... } запускается не сразу, а console.log(myfiles) работает.

В какой-то более поздний момент времени myfiles будет содержатьтребуемая информация.

Следует отметить, что files уже является массивом, поэтому нет смысла вручную добавлять каждый элемент в какой-либо другой пустой массив.Если идея состоит в том, чтобы собрать воедино результаты нескольких вызовов, используйте .concat;если вы просто хотите получить данные один раз, то вы можете просто назначить myfiles = files напрямую.

В целом, вам действительно следует прочитать "Стиль передачи продолжения" .

0 голосов
/ 12 июля 2017

Я столкнулся с той же проблемой, и, основываясь на ответах, приведенных в этом посте, я решил ее с помощью Обещаний , которые, кажется, идеально подходят в этой ситуации:

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
  });
});

Примечание: вам не нужно помещать найденные файлы в новый массив, потому что вы уже получаете массив из обратного вызова fs.readdir () 'c. По узлу документы :

Обратный вызов получает два аргумента (ошибки, файлы) , где файлы - это массив имен файлов в каталоге, исключая '.' и "..".

Я верю, что это очень элегантное и удобное решение, и самое главное - оно не требует от вас ввода и обработки новых модулей в ваш скрипт.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...