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

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

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

问题

以下是您要翻译的JavaScript代码的部分:

  1. function buildSettings(settings_file_names){
  2. return new Promise(function(resolve, reject){
  3. let settings = {};
  4. settings.files = {};
  5. settings.getFile = function(file_name){
  6. return this.files[file_name];
  7. }
  8. settings_file_names.forEach((file_name) => {
  9. settings.files[file_name] = getLocalFile(file_name);
  10. //getLocalFile() returns a promise.
  11. });
  12. Promise.all(Object.values(settings.files)).then(() => {
  13. for (let filename in settings.files){
  14. if (filename.includes(".json")){
  15. settings.files[filename].then((filedata) => {
  16. if (filedata){
  17. console.log("Parsing into JSON!");
  18. settings.file[filename] = JSON.parse(filedata);
  19. }else{
  20. settings.file[filename] = null;
  21. }
  22. });
  23. }
  24. }
  25. console.log("About to resolve!");
  26. resolve(settings);
  27. });
  28. });
  29. }

希望这对您有所帮助。

英文:

I have a Javascript function that looks like this in a personal project:

  1. function buildSettings(settings_file_names){
  2. return new Promise(function(resolve, reject){
  3. let settings = {};
  4. settings.files = {};
  5. settings.getFile = function(file_name){
  6. return this.files[file_name];
  7. }
  8. settings_file_names.forEach((file_name) => {
  9. settings.files[file_name] = getLocalFile(file_name);
  10. //getLocalFile() returns a promise.
  11. });
  12. Promise.all(Object.values(settings.files)).then(() => {
  13. for (let filename in settings.files){
  14. if (filename.includes(".json")){
  15. settings.files[filename].then((filedata) => {
  16. if (filedata){
  17. console.log("Parsing into JSON!");
  18. settings.file[filename] = JSON.parse(filedata);
  19. }else{
  20. settings.file[filename] = null;
  21. }
  22. });
  23. }
  24. }
  25. console.log("About to resolve!");
  26. resolve(settings);
  27. });
  28. });
  29. }

And the console output looks like this:

  1. utility_functions.js:148 About to resolve!
  2. 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 数据结构,然后稍后对其进行更改。将“构造”推迟到所有已解析的承诺之后。

您可以这样做:

  1. function buildSettings(settings_file_names) {
  2. const promises = settings_file_names.map(getLocalFile);
  3. // 返回您获得的承诺,而不是创建一个新的承诺
  4. return Promise.all(promises).then((responses) => { // 使用参数!
  5. const pairs = settings_file_names.map((file_name) => {
  6. const filedata = responses.shift(); // 获取响应!
  7. if (file_name.endsWith(".json")) {
  8. console.log("Parsing into JSON!");
  9. return [file_name, JSON.parse(filedata)];
  10. } else { // 其他解析器,如 CSV...
  11. return [file_name, null];
  12. }
  13. });
  14. console.log("About to resolve!");
  15. // 只有在完成时才构造设置数据结构。
  16. // 并返回它,以便它成为整体已解析的值
  17. return {
  18. files: Object.fromEntries(pairs),
  19. getFile(file_name) {
  20. return this.files[file_name];
  21. }
  22. };
  23. });
  24. }

请注意,我已经将代码示例中的 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:

  1. function buildSettings(settings_file_names) {
  2. const promises = settings_file_names.map(getLocalFile);
  3. // Return the promise you get instead of creating a new one
  4. return Promise.all(promises).then((responses) => { // Use the argument!
  5. const pairs = settings_file_names.map((file_name) => {
  6. const filedata = responses.shift(); // Get the response!
  7. if (file_name.endsWith(".json")) {
  8. console.log("Parsing into JSON!");
  9. return [file_name, JSON.parse(filedata)];
  10. } else { // Other parsers like CSV...
  11. return [file_name, null];
  12. }
  13. });
  14. console.log("About to resolve!");
  15. // Only construct the settings data structure when finished.
  16. // And return it, so it becomes the overal resolved value
  17. return {
  18. files: Object.fromEntries(pairs),
  19. getFile(file_name) {
  20. return this.files[file_name];
  21. }
  22. };
  23. });
  24. }

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:

确定