英文:
How do I wait for all then()s to resolve within an overarching Promise before resolve()ing that Promise?
问题
以下是您要翻译的JavaScript代码的部分:
function buildSettings(settings_file_names){
return new Promise(function(resolve, reject){
let settings = {};
settings.files = {};
settings.getFile = function(file_name){
return this.files[file_name];
}
settings_file_names.forEach((file_name) => {
settings.files[file_name] = getLocalFile(file_name);
//getLocalFile() returns a promise.
});
Promise.all(Object.values(settings.files)).then(() => {
for (let filename in settings.files){
if (filename.includes(".json")){
settings.files[filename].then((filedata) => {
if (filedata){
console.log("Parsing into JSON!");
settings.file[filename] = JSON.parse(filedata);
}else{
settings.file[filename] = null;
}
});
}
}
console.log("About to resolve!");
resolve(settings);
});
});
}
希望这对您有所帮助。
英文:
I have a Javascript function that looks like this in a personal project:
function buildSettings(settings_file_names){
return new Promise(function(resolve, reject){
let settings = {};
settings.files = {};
settings.getFile = function(file_name){
return this.files[file_name];
}
settings_file_names.forEach((file_name) => {
settings.files[file_name] = getLocalFile(file_name);
//getLocalFile() returns a promise.
});
Promise.all(Object.values(settings.files)).then(() => {
for (let filename in settings.files){
if (filename.includes(".json")){
settings.files[filename].then((filedata) => {
if (filedata){
console.log("Parsing into JSON!");
settings.file[filename] = JSON.parse(filedata);
}else{
settings.file[filename] = null;
}
});
}
}
console.log("About to resolve!");
resolve(settings);
});
});
}
And the console output looks like this:
utility_functions.js:148 About to resolve!
utility_functions.js:135 Parsing into JSON! (x8)
However, I need to wait to extract the values of those promises and parse them into JSON before resolving the overarching promise. I need to wait for what happens in the then()s. Is there a way to do this? Or am I facing this issue because I'm doing something wrong in a broader sense?
I expected then() to give me the value of a Promise synchronously if I already waited for that Promise to be fulfilled (as I think I did with Promise.all()). In reality then() seems to behave asynchronously even if the Promise is fulfilled.
Thank you in advance for any insight!
答案1
得分: 2
给定作为参数传递给 settings.files[filename].then
的回调函数是异步执行的,即使承诺已经被解析(正如在这里的情况)。这就是为什么事情的顺序会出错的原因。
解决方法很简单:Promise.all
返回一个承诺,该承诺会履行一个值,即所有承诺的已履行值的数组。因此,不再需要调用上面引用的 then
。
其次,甚至不需要使用 new Promise
创建一个新的“全局”承诺:这是Promise 构造函数反模式。思路是 Promise.all
已经返回了一个承诺,您可以直接使用它。
最后,我不会首先创建 settings
数据结构,然后稍后对其进行更改。将“构造”推迟到所有已解析的承诺之后。
您可以这样做:
function buildSettings(settings_file_names) {
const promises = settings_file_names.map(getLocalFile);
// 返回您获得的承诺,而不是创建一个新的承诺
return Promise.all(promises).then((responses) => { // 使用参数!
const pairs = settings_file_names.map((file_name) => {
const filedata = responses.shift(); // 获取响应!
if (file_name.endsWith(".json")) {
console.log("Parsing into JSON!");
return [file_name, JSON.parse(filedata)];
} else { // 其他解析器,如 CSV...
return [file_name, null];
}
});
console.log("About to resolve!");
// 只有在完成时才构造设置数据结构。
// 并返回它,以便它成为整体已解析的值
return {
files: Object.fromEntries(pairs),
getFile(file_name) {
return this.files[file_name];
}
};
});
}
请注意,我已经将代码示例中的 HTML 实体 "
替换为了正常的引号字符 "
,以使代码更具可读性。
英文:
The callback that is given as argument to settings.files[filename].then
executes asynchronously, even when the promise is already resolved (as is the case here). That is why things happen in the wrong order.
The solution is simple: Promise.all
returns a promise that fulfils with a value: that is an array of all the promises' fulfilled values. So there is no more need to call the above quoted then
.
Secondly, you don't even need to create a new "overarching" promise with new Promise
: this is the Promise constructor antipattern. The idea is that Promise.all
already returns a promise, and you work with that one.
Finally, I would not first create the settings
data structure only to mutate it later. Delay that "structuring" until you have all resolved promises.
You can do it as follows:
function buildSettings(settings_file_names) {
const promises = settings_file_names.map(getLocalFile);
// Return the promise you get instead of creating a new one
return Promise.all(promises).then((responses) => { // Use the argument!
const pairs = settings_file_names.map((file_name) => {
const filedata = responses.shift(); // Get the response!
if (file_name.endsWith(".json")) {
console.log("Parsing into JSON!");
return [file_name, JSON.parse(filedata)];
} else { // Other parsers like CSV...
return [file_name, null];
}
});
console.log("About to resolve!");
// Only construct the settings data structure when finished.
// And return it, so it becomes the overal resolved value
return {
files: Object.fromEntries(pairs),
getFile(file_name) {
return this.files[file_name];
}
};
});
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论