如何使用asynchronous函数作为Array.filter()的比较函数?

对于我正在写的Node JS模块,我想使用asynchronous函数Stats.isFile()作为Array.filter()函数的callback函数。 下面我有一个我想要实现的工作示例,但使用snychronous等价物。 我无法弄清楚如何包装asynchronous函数,以便在Array.filter()函数内变得可用。

 const fs = require('fs') exports.randomFile = function(dir = '.') { fs.readdir(dir, (err, fileSet) => { const files = fileSet.filter(isFile) const rnd = Math.floor(Math.random() * files.length); return files[rnd]) }) } function isFile(item) { return (fs.statSync(item)).isFile() } 

您不能使用.filter()的asynchronouscallback。 .filter()需要一个同步结果,并且无法从asynchronous操作中获取同步结果。 所以,如果你打算使用asynchronousfs.stat() ,那么你将不得不使整个操作asynchronous。

这是一种方法。 请注意,即使randomFile()需要asynchronous通信它的结果。 这里我们使用一个callback。

 const path = require('path'); const fs = require('fs'); exports.randomFile = function(dir, callback) { fs.readdir(dir, (err, files) => { if (err) return callback(err); function checkRandom() { if (!files.length) { // callback with an empty string to indicate there are no files return callback(null, ""); } const randomIndex = Math.floor(Math.random() * files.length); const file = files[randomIndex]; fs.stat(path.join(dir, file), (err, stats) => { if (err) return callback(err); if (stats.isFile()) { return callback(null, file); } // remove this file from the array files.splice(randomIndex, 1); // try another random one checkRandom(); }); } checkRandom(); }); } 

而且,您将如何使用另一个模块的asynchronous接口。

 // usage from another module: var rf = require('./randomFile'); fs.randomFile('/temp/myapp', function(err, filename) { if (err) { console.log(err); } else if (!filename) { console.log("no files in /temp/myapp"); } else { console.log("random filename is " + filename); } }); 

如果你走asynchronous,你必须从入口点开始asynchronous,所以randomFile需要一些方法来“返回”一个asynchronous值(通常是通过callback,承诺或stream)。

我不知道你的文件结构是如何看起来像,而不是检查作为一个文件的所有条目,我会select一个随机的条目,检查是否是一个文件,如果不是再试一次。

这可能看起来像这样

 const fs = require('fs'); const path = require('path'); exports.randomFile = (dir = '.', cb) => { fs.readdir(dir, (err, files) => { if (err) { return cb(err); } pickRandom(files.map((f) => path.join(dir, f)), cb); }); } function pickRandom (files, cb) { const rnd = Math.floor(Math.random() * files.length); const file = files[rnd]; fs.stat(file, (err, stats) => { if (err) { return cb(err); } if (stats.isFile()) { return cb(null, file); } else { return pickRandom(files, cb); } }) }