我有两个 promise 。一个读取 sample.txt
文件,另一个读取 /books/
文件夹中的所有文件。第二个 Promise 使用名为 readFiles
的函数,该函数获取目录名并使用它们来查看每个文件。当所有的 promise 都准备好后,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
总是被拒绝:
reason: [ 'archive 1\n' ]
我做错了什么?
最佳答案
Promise 是一次性设备。一旦它们被拒绝或解决,它们的状态就永远不会改变。考虑到这一点,readFiles()
为它读取的每个文件调用它的回调,并且每次调用该回调时您都会拒绝或解决,但是您使用它的方式会检查:
if (archives.length === result.length)
第一个永远不会成立,然后你就拒绝了。一旦该 promise 被拒绝,其状态就无法改变。后续调用回调也会调用reject()
然后最后一个会调用resolve()
,但状态早已设置,因此只有第一次调用 reject()
或resolve()
实际上做任何事情。其他人则被简单地忽略。所以,p1
总是会拒绝,因此Promise.all()
使用 p1
总是会拒绝。
您需要更改readFiles()
要么只在完成所有文件时调用回调一次,要么将其更改为返回一个在读取所有文件时解析的 promise ,或者更改使用回调的方式,这样您就不会在第一次时拒绝它被调用。
一般来说,如果您要使用 Promise,那么您希望在最低级别进行 Promise,并在任何地方使用 Promise 的优势(特别是错误传播),而不是混合回调和 Promise。为此,我建议:
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()
另外,你会得到这个:
// 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);
});
<小时/>
如果您使用 Bluebird promise library这使得一次性 Promise 整个模块变得很容易,并且有一些额外的功能来管理 Promise 流程控制,那么上面的代码就简化为:
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()
一行代码创建 fs
上每个方法的 promise 版本模块带有Async
他们的后缀。在这里,我们使用 fs.readFileAsync()
和fs.readdirAsync()
所以我们可以使用 Promise 来完成所有事情。
关于javascript - 由于 Promise 拒绝,Promise.all 永远不会被触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40821198/