2011-06-09 49 views
6

因此,我试图使用nodejs express FS模块迭代我的应用程序中的目录,将每个文件名存储在数组中,我可以传递给我的快速视图并遍历名单,但Im努力这样做。当我这样做files.forEach功能环路内的console.log,它打印的文件名就好了,但只要我尝试做任何事情,如:nodejs express fs迭代文件到数组或对象失败

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

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

回答

30

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

+1深入挖掘并解释您应该如何使用回调。 – 2011-06-09 04:03:42

+0

字,正是我需要的,谢谢! – thrice801 2011-06-09 05:41:49

+0

只需使用readdir的同步版本,而不需要等待它完成:[fs.readdirSync](http://nodejs.org/docs/v0.3.1/api/fs.html#fs.readdirSync) – Automatico 2013-04-20 23:49:56

5

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内的每个文件执行某些操作。

2

我认为这与回调函数来执行,

没错。

fs.readdir为该信息向文件系统发出异步请求,并在以后的某个时间用结果调用回调。

因此function (err, files) { ... }不立即运行,但console.log(myfiles)呢。

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

你应该注意到,files已经是一个数组了,所以将每个元素手动添加到其他空白数组中真的没有意义。如果想法是将多个调用的结果放在一起,则使用.concat;如果你只想获得一次数据,那么你可以直接分配myfiles = files。总的来说,你真的应该阅读"Continuation-passing style"

3

正如前面提到的,你使用的是异步方法,所以你有一个不确定的执行路径。

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

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

+0

↑为最简单的答案。 – 2015-02-02 23:00:29

0

我面临同样的问题,并立足于这个职位,我已经与承诺,这似乎是在这种情况下完美使用的解决了这个问题给出答案:

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回调。根据节点docs

回调有两个参数(ERR文件)其中文件是一个数组中不包括目录中的文件的名称 “”和'..'。

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