[英]Return when first promise resolves
I have a bunch of file names in an array, and would like to read the contents of the first of the files that exists. 我在数组中有一堆文件名,并且想读取存在的第一个文件的内容。 They're config files, so it's important that the order is deterministic, so I can't use .race()
. 它们是配置文件,因此顺序是确定性很重要,因此我不能使用.race()
。 The version I have below maps over each file in order, tries to load it, and if it loads successfully, calls resolve. 我下面的版本按顺序映射每个文件,尝试加载它,如果加载成功,则调用resolve。
Here are a couple of issues with this implementation: 以下是此实现的几个问题:
resolve(...)
doesn't actually exit the loop, so the program opens every file in the list, even when doesn't need to. 调用resolve(...)
实际上并不会退出循环,因此该程序即使在不需要时也会打开列表中的每个文件。 this is required to reject when we don't receive any files
) seems like a hack. 拒绝条件( this is required to reject when we don't receive any files
拒绝的条件),看起来像是黑客。 However, if it's not here, the promise is never rejected. 但是,如果不在这里,那么承诺就永远不会被拒绝。 Are there any better ways to do structure this? 有没有更好的方法来构造此结构? I could probably do it with a single Promise.filter
call, but I don't want to query every file if I don't need to. 我可能只用一个Promise.filter
调用就可以做到这Promise.filter
,但是如果不需要的话,我不想查询每个文件。
Thanks 谢谢
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)
})
If you want sequential iteration, just use a recursive approach: 如果要顺序迭代,只需使用递归方法:
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)
})
If you want to try to read them all in parallel and the select the first successful in the list, you can use Promise.map
+ .reflect()
and then just filter the results (eg via _.find
). 如果要尝试并行读取所有内容并选择列表中的第一个成功对象,则可以使用Promise.map
+ Promise.map
.reflect()
然后仅过滤结果(例如,通过_.find
)。
This can be achieved by recursion but also by building a catch
chain using Array#reduce(): 这可以通过递归来实现,也可以通过使用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);
});
The catch chain ensures that every time fs.readFileAsync(path, 'utf-8')
fails, the next path is tried. 捕获链确保每次fs.readFileAsync(path, 'utf-8')
失败时,都尝试下一个路径。
The first successful fs.readFileAsync(path, 'utf-8')
will drop through to .then(function([filename, configFile]) {...}
. 第一个成功的fs.readFileAsync(path, 'utf-8')
将进入.then(function([filename, configFile]) {...}
。
Total failure will drop through to .catch(function(err) {...}
. 完全失败将落入.catch(function(err) {...}
。
There is this hackish approach to solve this problem neatly. 有这种骇人听闻的方法可以巧妙地解决这个问题。 You may invert
the promises like; 您可以像这样invert
承诺。
var invert = pr => pr.then(v => Promise.reject(v), x => Promise.resolve(x));
which in fact comes handy when used with Promise.all()
to get the first resolving promise by ignoring the rejected ones. 实际上,当与Promise.all()
一起使用时, Promise.all()
可以通过忽略被拒绝的对象来获得第一个解决承诺,因此非常方便。 I mean when inverted, all promises rejected (resolved) may go unnoticed while the first resolving (rejecting) one gets caught at the .catch()
stage of Promise.all()
. 我的意思是倒立时,拒绝了所有的承诺(解决)可能会被忽视,而第一解析(拒绝)一个被在抓.catch()
阶段Promise.all()
Cool..! 凉..!
Watch this; 看这个;
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}`));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.