2011-06-09 73 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文件)其中文件是一個數組中不包括目錄中的文件的名稱 「」和'..'。

我相信這是非常優雅和方便的解決方案,最重要的是 - 它不要求您爲腳本引入和處理新模塊。