【问题标题】:How to correctly use Promise.all to combine two datasets and return a single object?如何正确使用 Promise.all 组合两个数据集并返回单个对象?
【发布时间】:2021-02-17 12:50:00
【问题描述】:

我有一个现有对象data。 我正在使用 NodeJS async 函数来检索 fetchResponse 的值。 最后,我尝试使用以下for loopfetchResponse 嵌入到现有的data 对象中

for (let key in data) {
    for (let number in data[key]) {
        data[key][number].map( async d=> {
            const fetchResponse = await sequelize
                .query(await fetchHostDetails(d.Hostname), options);
        data[key][number]['Fetchresult'] =  fetchResponse;
        });
    }
}
return data;

我看到 data 仍然具有原始值,不包括 key value pair Fetchresult : fetchResponse

我正在尝试使用Promise.All 如下,但它仍然给出原始data 对象值。

for (let key in data) {
    for (let number in data[key]) {
        data[key][number].map( async d=> {
            const fetchResponse = await sequelize
                .query(await fetchHostDetails(d.Hostname), options);
             await Promise.all({...d, Fetchresult : fetchResponse});
        });
    }
}
return data;

const data =
{
  "error1": {
    "7": [
      {
        "ErrorType": "Error-1A",
        "Hostname": "host123.com"
      }
    ],
    "8": [
      {
        "ErrorType": "Error-1B",
        "Hostname": "host223.com"
      },
      {
        "ErrorType": "Error-1C",
        "Hostname": "host1231.com"
      }
    ]
  }
}

对于Error-1A 下的主机名,我正在低于fetchResponse

const fetchResponse =
    [
      {
        "resType": "unknow data",
        "res": "missing data"
      },
      {
        "resType": "login failed",
        "res": "login with wrong userid"
      }
    ]

同样,Error-1bError-1C 下的主机名也将有单独的 fetchResponse,但响应格式将类似于我们为 Error-1A 获得的格式。

对于data.error1[8],我们得到fetchResponse 的2 个值,但请注意,它们是Error-1BError-1C 的2 个独立值。一个显示在Error-1B之后,另一个显示在Error-1C之后。

【问题讨论】:

  • map 返回一个数组。如果您没有使用返回的数组,则说明您错误地使用了map。在这种情况下,您可以使用for await .. of(不带Promise.all)来帮助您完成您的工作,尽管您正在做的事情还不清楚。
  • 我的问题更加清晰。请看一下
  • 对于data.error1[8],您将获得多个fetchResponses(每个主机名一个)。你到底想用那个fetchResponse做什么?
  • 对于data.error1[8],我们得到fetchResponse 的2 个值,但请注意,它们是Error-1BError-1C 的2 个独立值。 Error-1B之后显示一个,Error-1C之后显示另一个。

标签: javascript node.js arrays json dictionary


【解决方案1】:

在循环中使用await 对我来说没有意义。我更愿意生成一个单一的、扁平的 Promise 列表,最终有一个 Promise.all 等待它们。

请允许我使用placeholderAsyncOperation 代替您使用密钥和号码进行提取。出于演示目的,我将稍微延迟一下,然后根据参数返回一些对象。不过,我会使用您的原始输入 data

const data =
{
  "error1": {
    "7": [
      {
        "ErrorType": "Error-1A",
        "Hostnames": "host123.com,hostabc.com,host33a.com..."
      }
    ],
    "8": [
      {
        "ErrorType": "Error-1B",
        "Hostnames": "host223.com,host2c.com,host43a.com..."
      },
      {
        "ErrorType": "Error-1C",
        "Hostnames": "host1231.com,host2abc.com,host313a.com..."
      }
    ]
  },
  "error2": {
  },
  "error3": {
  }
}

async function placeholderAsyncOperation(key, number, result) {
  await delay(1000);
  return {key, number, resultJSON: JSON.stringify(result)};
}

const delay = time_ms => new Promise(resolve => setTimeout(resolve, time_ms));


let promises = [];
for (let key in data) {
  for (let number in data[key]) {
    promises.push(data[key][number].map(val => (placeholderAsyncOperation(key, number, val))));
  }
}

Promise.all(promises.flat()).then(result => console.table(result));

注意:关于这个 sn-p:

这使用了console.table,Stack Overflow 还不能很好地支持(嵌入到页面中的输出将显示为空),所以如果你在浏览器中运行这个 sn-p,你可能必须 在运行 sn-p 之前打开浏览器的内置控制台。比如,通过按 F12 或者您的浏览器显示其控制台。

【讨论】:

  • 我不想使用await delay(1000); 也是后端NodeJS代码所以不会在浏览器上运行它
  • @meallhour 正如函数名所说,这只是一个 placeholderAsyncOperation 用于演示目的。在那里做你的获取和数据库的东西。
【解决方案2】:

我首先会构建一个包含所有承诺以及键和索引的数组:

let promises = [];

for (let key in data) {
  for (let number in data[key]) {
      let p = async() => {
           const fetchResponse = await sequelize.query(
               await fetchHostDetails(d.Hostname), options
           );
           return {key, number, fetchResponse};
      };
      promises.push(p);
   }
}

现在,在您将这个“获取”数据注入原始数据之前,需要解决 Promise。所以我们使用异步函数:

const merge = async (data, promises) => {
    
    // wait for all promises
    const resolvedPromises = await Promise.all(promises);

    // inject the results into "data"
    resolvedPromises.forEach(row => {
        if (row) {
            const {key, number, fetchResponse} = row;
            data[key][number]['Fetchresult'] =  fetchResponse;
        }
    });

    return data;

};

当你调用它时,记住它是异步的:

merge(data, promises).then(newData => {

   console.log(newData);

});

【讨论】:

  • 我收到一个错误Error: undefined : TypeError: data.map is not a function
  • 我明白了。我假设 data[key] 是一个数组。试试我刚刚编辑的更新代码
  • 在某些情况下fetchResponse 为空,我收到一个错误 - TypeError: Cannot set property 'Fetchresult' of undefined 您能否也为fetchResponse 添加空验证检查?
  • 我已经添加了这个逻辑。你觉得好看吗? resolvedPromises.forEach(row => { const {key, index, fetchResponse} = row; const fetchData = fetchResponse == null ? "No Notes Yet" : fetchResponse; data[key][index]['Fetchresult'] = fetchData; });
  • Error: undefined : TypeError: Cannot read property 'undefined' of undefined
猜你喜欢
  • 2021-02-16
  • 2021-02-10
  • 1970-01-01
  • 2013-01-17
  • 2022-01-04
  • 1970-01-01
  • 2021-09-12
相关资源
最近更新 更多