我有两个承诺。一个读取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' ]
我做错了什么?
答案 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
后缀。在这里,我们使用Async
和fs.readFileAsync()
,因此我们可以将承诺用于所有内容。