Return 当第一个承诺解决时
Return when first promise resolves
目标
我在一个数组中有一堆文件名,我想读取第一个存在的文件的内容。它们是配置文件,所以确定顺序很重要,所以我不能使用 .race()
。我下面的版本按顺序映射每个文件,尝试加载它,如果加载成功,调用 resolve。
问题
以下是此实现的几个问题:
- 调用
resolve(...)
实际上并没有退出循环,所以程序会打开列表中的每个文件,即使不需要。
- 拒绝条件(在
this is required to reject when we don't receive any files
)似乎是一个 hack。然而,如果它不在这里,承诺永远不会被拒绝。
- 解析代码看起来像一个 promise 反模式。
有没有更好的方法来构建这个?我可能可以通过单个 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)
})
如果要顺序迭代,只需使用递归方法:
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)
})
如果你想尝试并行读取它们并且 select 在列表中第一个成功,你可以使用 然后只过滤结果(例如通过 _.find
).
这可以通过递归实现,也可以通过使用 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) {...}
。
有这个 hackish 方法巧妙地解决了这个问题。你可以invert
这样的承诺;
var invert = pr => pr.then(v => Promise.reject(v), x => Promise.resolve(x));
当与 Promise.all()
一起使用时,它实际上会派上用场,通过忽略被拒绝的承诺来获得第一个解决承诺。我的意思是,当倒置时,所有被拒绝(已解决)的承诺可能会被忽视,而第一个已解决(拒绝)的承诺会在 Promise.all()
的 .catch()
阶段被捕获。酷..!
观看这个;
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}`));
目标
我在一个数组中有一堆文件名,我想读取第一个存在的文件的内容。它们是配置文件,所以确定顺序很重要,所以我不能使用 .race()
。我下面的版本按顺序映射每个文件,尝试加载它,如果加载成功,调用 resolve。
问题
以下是此实现的几个问题:
- 调用
resolve(...)
实际上并没有退出循环,所以程序会打开列表中的每个文件,即使不需要。 - 拒绝条件(在
this is required to reject when we don't receive any files
)似乎是一个 hack。然而,如果它不在这里,承诺永远不会被拒绝。 - 解析代码看起来像一个 promise 反模式。
有没有更好的方法来构建这个?我可能可以通过单个 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)
})
如果要顺序迭代,只需使用递归方法:
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)
})
如果你想尝试并行读取它们并且 select 在列表中第一个成功,你可以使用 _.find
).
这可以通过递归实现,也可以通过使用 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) {...}
。
有这个 hackish 方法巧妙地解决了这个问题。你可以invert
这样的承诺;
var invert = pr => pr.then(v => Promise.reject(v), x => Promise.resolve(x));
当与 Promise.all()
一起使用时,它实际上会派上用场,通过忽略被拒绝的承诺来获得第一个解决承诺。我的意思是,当倒置时,所有被拒绝(已解决)的承诺可能会被忽视,而第一个已解决(拒绝)的承诺会在 Promise.all()
的 .catch()
阶段被捕获。酷..!
观看这个;
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}`));