How do I wait for all then()s to resolve within an overarching Promise before resolve()ing that Promise?

huangapple go评论104阅读模式
英文:

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];
            }
        };
    });
}

huangapple
  • 本文由 发表于 2023年8月10日 22:58:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76876967.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定