由于承诺拒绝,Promise.all永远不会被触发

时间:2016-11-26 17:43:29

标签: javascript promise

我有两个承诺。一个读取sample.txt文件,另一个读取/books/文件夹中的所有文件。第二个promise使用一个名为readFiles的函数,它接受dirnames并使用它们查看每个文件。当所有承诺都准备好后,then内的代码应该运行:

const p1 = new Promise((resolve, reject) => {
  fs.readdir(__dirname + '/books/', (err, archives) => {
    // archives = [ 'archive1.txt', 'archive2.txt']
    readFiles(archives, result => {
      if (archives.length === result.length) resolve(result)
      else reject(result)
    })
  })
})

const p2 = new Promise((resolve, reject) => {
  fs.readFile('sample.txt', 'utf-8', (err, sample) => {
    resolve(sample)
  })
})

Promise.all([p1, p2]).then(values => {
  console.log('v:', values)
}).catch(reason => {
  console.log('reason:', reason)
})

function readFiles (archives, callback) {
  const result = []
  archives.forEach(archive => {
    fs.readFile(__dirname + '/books/' + archive, 'utf-8', (err, data) => {
      result.push(data)
      callback(result)
    })
  })
}

但是,Promise.all总是被拒绝:

  

原因:['存档1 \ n' ]

我做错了什么?

1 个答案:

答案 0 :(得分:3)

Promise是一次性设备。一旦他们被拒绝或解决,他们的状态永远不会改变。考虑到这一点,readFiles()为每个读取的文件调用它的回调,并且每次调用回调时拒绝或解析,但是你使用它的方式,你检查:

if (archives.length === result.length)

在第一个上永远不会是真的然后你拒绝。一旦该承诺被拒绝,其状态就不会改变。对回调的后续调用也会调用reject(),然后最后一个会调用resolve(),但状态很长,因此只有第一次调用reject()resolve()实际上做了什么。其他人完全被忽略了。因此,p1将始终拒绝,因此使用Promise.all()的{​​{1}}将始终拒绝。

您需要将p1更改为仅在完成所有文件时调用其回调一次,或者更改它以返回单个承诺,该承诺在读取所有文件时解析或更改您的方式'重新使用回调,这样你就不会在第一次被拒绝时拒绝。

一般来说,如果您要使用promises,那么您希望在最低级别进行promisify并使用promises(特别是错误传播)的优势,而不是混合回调和promise。为此,我建议:

readFiles()

或者,更深入一级并宣传fs.readFileP = function(fname, encoding) { return new Promise(function(resolve, reject) { fs.readFile(fname, encoding, function(err, data) { if (err) return reject(err); resolve(data); }); }); } function readFiles(archives, encoding, callback) { return Promise.all(archives.map(function(file) { return fs.readFileP(file, encoding); })); } ,你也会得到这个:

fs.readdir()

如果您使用Bluebird promise library,这样可以轻松地立即宣传整个模块并具有一些管理Promise流量控制的额外功能,那么上面的代码就简化了:

// helper functions
fs.readdirP = function(dir) {
    return new Promise(function(resolve, reject) {
        fs.readdir(dir, function(err, files) {
            if (err) return reject(err);
            resolve(files);
        });
    });
}

fs.readFileP = function(fname, encoding) {
    return new Promise(function(resolve, reject) {
        fs.readFile(fname, encoding, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}

function readFiles(archives, encoding) {
    encoding = encoding || 'utf8';
    return Promise.all(archives.map(function(file) {
        return fs.readFileP(file, encoding);
    }));
}

// actual logic for your operation
const p1 = fs.readdirP(__dirname + '/books/').then(readFiles);
const p2 = fs.readFileP('sample.txt', 'utf-8');

Promise.all([p1, p2]).then(values => {
  console.log('v:', values);
}).catch(reason => {
  console.log('reason:', reason);
});

在这段代码中,const Promise = require('bluebird'); const fs = Promise.promisifyAll(require('fs')); const p1 = fs.readdirAsync(__dirname + '/books/').then(files => { return Promise.map(archives, file => { return fs.readFileAsync(file, 'utf8'); }); }); const p2 = fs.readFileAsync('sample.txt', 'utf-8'); Promise.all([p1, p2]).then(values => { console.log('v:', values); }).catch(reason => { console.log('reason:', reason); }); 代码行在Promise.promisifyAll()模块上创建了每个方法的promisified版本,并带有fs后缀。在这里,我们使用Asyncfs.readFileAsync(),因此我们可以将承诺用于所有内容。