我在数组中有一堆文件名,并且想要读取存在的第一个文件的内容。他们配置文件,因此订单具有确定性非常重要,因此我无法使用.race()
。我下面的版本按顺序映射每个文件,尝试加载它,如果加载成功,则调用解析。
以下是此实施的几个问题:
resolve(...)
实际上并不退出循环,因此程序会打开列表中的每个文件,即使不需要也是如此。 this is required to reject when we don't receive any files
)似乎是一种黑客行为。但是,如果它不在这里,承诺永远不会被拒绝。 有没有更好的方法来构建这个?我可以通过一次Promise.filter
电话来完成,但如果我不需要,我也不想查询每个文件。
由于
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var _ = require('lodash');
new Promise((resolve, reject) => {
// Resolve with the first of the files below that exists
return Promise.mapSeries(
['./file_that_doesntexist.json', '../file_that_might_exist.json', './file_that_doesnt_exist_either.json', '../file_that_exists.json']
, (filename) => fs.readFileAsync(filename, 'utf-8')
.then(file => {
resolve([filename, file]);
return true;
})
.catch(_.stubFalse)
)
.then(files => { // this is required to reject when we don't receive any files
if(!files.some(x => x))
reject('did not receive any files');
});
})
.then(function([filename, configFile]) {
// do something with filename and configFile
})
.catch(function(err) {
console.error(err)
})
答案 0 :(得分:1)
如果您想要顺序迭代,只需使用递归方法:
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
function readFirstOf(filenames)
if (!filenames.length)
return Promise.reject(new Error('did not receive any files'));
return fs.readFileAsync(filenames[0], 'utf-8')
.then(file =>
[filenames[0], file]
, err =>
readFirstOf(filenames.slice(1))
);
}
readFirstOf(['./file_that_doesntexist.json', '../file_that_might_exist.json', './file_that_doesnt_exist_either.json', '../file_that_exists.json'])
.then(function([filename, configFile]) {
// do something with filename and configFile
})
.catch(function(err) {
console.error(err)
})
如果您想尝试并行读取它们并在列表中选择第一个成功,您可以使用Promise.map
+ .reflect()
然后只过滤结果(例如通过_.find
)。
答案 1 :(得分:1)
这可以通过递归实现,也可以通过使用Array#reduce()构建catch
链来实现:
var paths = ['./file_that_doesntexist.json', '../file_that_might_exist.json', './file_that_doesnt_exist_either.json', '../file_that_exists.json'];
// Resolve with the first of the files below that exists
paths.reduce(function(promise, path) {
return promise.catch(function(error) {
return fs.readFileAsync(path, 'utf-8').then(file => [path, file]);
});
}, Promise.reject())
.then(function([filename, configFile]) {
// do something with filename and configFile
})
.catch(function(err) {
console.error('did not receive any files', err);
});
捕获链确保每次fs.readFileAsync(path, 'utf-8')
失败时,都会尝试下一个路径。
第一个成功的fs.readFileAsync(path, 'utf-8')
将逐渐转移到.then(function([filename, configFile]) {...}
。
总失败将降至.catch(function(err) {...}
。
答案 2 :(得分:0)
有这种hackish方法可以整齐地解决这个问题。您可以invert
承诺,例如;
var invert = pr => pr.then(v => Promise.reject(v), x => Promise.resolve(x));
实际上,当与Promise.all()
一起使用时,通过忽略被拒绝的承诺来获得第一个解决承诺,这实际上很方便。我的意思是倒置时,所有拒绝(已解决)的承诺可能会被忽视,而第一个解决(拒绝)的承诺会被.catch()
Promise.all()
阶段捕获。酷..!
观看此内容;
var invert = pr => pr.then(v => Promise.reject(v), x => Promise.resolve(x)),
promises = [Promise.reject("No such file"),
Promise.reject("No such file either"),
Promise.resolve("This is the first existing files content"),
Promise.reject("Yet another missing file"),
Promise.resolve("Another file content here..!")];
Promise.all(promises.map(pr => invert(pr)))
.catch(v => console.log(`First successfully resolving promise is: ${v}`));