为什么这个fs.readFile循环没有将其结果推送到我的数组?

时间:2015-03-14 16:43:31

标签: javascript node.js fs

#!/usr/bin/env node

var fs = require('fs')
  , async = require('async') 
  , program = require('commander')

program
  .version('0.0.1')
  .usage('<keywords>')
  .parse(process.argv)

async.waterfall([
  fs.readdir.bind(fs, __dirname),
  parseHTML,
], saveResult)

function parseHTML(files, callback) {
  var result = []

  files.forEach(function(file) {
    if (file.match(/\.html$/)) {
      fs.readFile(file, 'utf8', function(err, data) {
        if (err) throw err
        result.push(data)
      })
    }
  })

  callback(null, result)
}

function saveResult(err, result) {
  console.log(result)
}

我很困惑因为console.log(data)确实输出了数据:

<p>File 1</p>

<p>File 2</p>

但最终的result是一个空数组:[]

为什么我做错了?

1 个答案:

答案 0 :(得分:1)

您必须等待查看结果,直到最后fs.readFile()操作完成。这些是异步操作,它们将来会完成一些时间。您正在检查结果,然后才能完成。

有很多方法可以解决这个问题,但是这种方法可能会对您的代码造成最小的改变,因为它只是对已完成的代码进行了反击:

function parseHTML(files, callback) {
    var result = [],
        cntr = 0;

    files.forEach(function(file) {
        if (file.match(/\.html$/)) {
            fs.readFile(file, 'utf8', function(err, data) {
                if (err) throw err
                result.push(data)
                    // see if we're done processing all the results
                    ++cntr;
                if (cntr === files.length) {
                    callback(null, result);
                }
            });
        } else {
            ++cntr;
            if (cntr === files.length) {
                callback(null, result);
            }
        }
    });
}

我个人更喜欢使用promises和Promise.all()来解决这个问题。

这是一个使用Bluebird promise库的版本,它保留了你的一些其他结构:

var Promise = require("bluebird");
var fs = Promise.promisifyAll(require('fs'));

// your other code here

function parseHTML(files, callback) {
    var promises = [];

    files.forEach(function(file) {
        if (file.match(/\.html$/)) {
            promises.push(fs.readFileAsync(file, 'utf8'));
    });
    Promise.all(promises).then(function(results) {
        // all results in results array
        callback(null, results);
    }, function(err) {
       // error here
    });
}

而且,这是一个完全承诺的版本:

var Promise = require("bluebird");
var fs = Promise.promisifyAll(require('fs'));

function parseHTML(files) {
    var promises = [];

    files.forEach(function(file) {
        if (file.match(/\.html$/)) {
            promises.push(fs.readFileAsync(file, 'utf8'));
    });
    return Promise.all(promises);
}

fs.readdirAsync(__dirname).then(parseHTML).then(function(results) {
    // files are in the results array here
}).catch(function(err) {
    // error here
});