【问题标题】:Wait for the loop to finish before performing the next action在执行下一个操作之前等待循环完成
【发布时间】:2020-04-28 18:52:40
【问题描述】:

我有以下循环获取数据,然后将其存储到allVegetables 变量中。在记录数组的长度之前,我需要完成循环。使用下面的代码,allVegetables 的长度为零

var allVegetables = [];

for (var i = 0; i < 10; i++) {

  //fetch the next batches of vegetables
  fetch(`https://www.nofrills.ca/api/category/NFR001001002000/products?pageSize=48&pageNumber=${i}&sort=title-asc`, {
    "headers": {
      ...      
    },
    "referrer": "https://www.nofrills.ca/Food/Fruits-%26-Vegetables/Vegetable/c/NFR001001002000?sort=title-asc",
    "referrerPolicy": "no-referrer-when-downgrade",
    "body": null,
    "method": "GET",
    "mode": "cors"
  }).then(
    function (response) {
      if (response.status !== 200) {
        console.log('Looks like there was a problem. Status Code: ' +
          response.status);
        return;
      }

      response.json().then(function (data) {
        //ad the results of the data to the array
        allVegetables = allVegetables.concat(data.results);
      });
    })
};

console.log("number of vegetables are:", allVegetables.length);

目前日志给我零,我认为这是因为它没有等待循环完成填充数组allVegetables。我还假设我应该使用异步,但我是新手,不知道如何做到这一点

【问题讨论】:

    标签: javascript node.js loops for-loop async-await


    【解决方案1】:

    尝试将所有获取请求及其结果存储在一个数组中。这将导致一系列承诺。有了这些 Promise,您就可以用 Promise.all 等待所有响应完成,并一次性处理所有响应的输出,并将它们全部存储在 allVegetables 变量中。

    因为您最终会得到一个数组数组,所以使用 Array.prototype.flat() 创建一个数组,其中包含您可以分配给您的 allVegetables 变量的所有值。

    let allVegetables = [];
    let iterations = 10;
    
    const requests = Array(iterations).fill().map((_, i) => fetch(`https://www.nofrills.ca/api/category/NFR001001002000/products?pageSize=48&pageNumber=${i}&sort=title-asc`, {
      "headers": {
        ...      
      },
      "referrer": "https://www.nofrills.ca/Food/Fruits-%26-Vegetables/Vegetable/c/NFR001001002000?sort=title-asc",
      "referrerPolicy": "no-referrer-when-downgrade",
      "body": null,
      "method": "GET",
      "mode": "cors"
    }).then(response => {
      if (response.status !== 200) {
        throw new Error('Looks like there was a problem with request ${i}. Status Code: ' + response.status);
      }
      return response.json();
    }).then(data => {
      return data.results;
    });
    
    const responses = Promise.all(requests).then(data => {
      allVegetables = [...allVegetables, ...data.flat()];
    }).catch(error => {
      console.log(error);
    });
    

    【讨论】:

    • 通读一切都是有道理的,但是当我运行它时它不起作用。 1. 我希望从 fetch 中仅获取一个名为 results 的数组,这就是为什么我将其写为 data.results 如何在您的代码中实现这一点? return response.results.json(); 之类的东西,但它似乎不正确。我应该总共得到 193,但现在你的它给了我 10
    • 我忽略了你的第一点。这可以通过向 Promise 链添加另一个 then 回调来解决,该回调仅在 Promise 的末尾返回 results 属性。在您示例的循环中,您迭代 10 次。您可以通过将 iterations 变量的值更改为 193 来调整它。
    • 只需要执行 10 次获取。每10次;响应数据中有一个名为 results 的数组,其中包含多达 48 个产品。当我说 193 时;我的意思是 193 种产品。基本上在某一时刻,提取在提取中返回空的results。所以我觉得我不需要在这里做iteration。现在就试试吧
    • 如果这些请求不需要来自彼此的任何数据,例如 fetch 2 需要 fetch 1 的结果,那么您可以更改循环以将所有 fetch 请求收集在一个数组中并添加那些在我的回答中循环之前对同一个数组的其他 3-4 个请求。这需要您将其重写回常规的for 循环和push 数组中的提取。如果他们确实需要彼此的结果,您需要在此之前调用 fetch 的 then 回调中的下一个 fetch。
    • 嗯,是的,就是这样。但请注意类型,因为在这种情况下,requests 值是一个数组。如果其他人也是,你需要连接。如果没有,请这样写:Promise.all([...requests, requests2, requests3, requests4])Promise.all 接受一系列承诺。
    【解决方案2】:

    您可以将所有 fetch Promise 存储在一个数组中,然后使用 Promise.allSettled 等待它们完成工作。

    这是一个简单的例子:

    const responses = [];
    for (let i = 0; i < 4; i++) {
      responses.push(
        fetch("https://jsonplaceholder.typicode.com/posts/1").then(response =>
          response.json()
        )
      );
    }
    Promise.allSettled(responses).then(console.log);
    

    这将记录具有此形状的对象数组:

    {
      status: 'string',
      value: Object
    }
    

    作为“值”属性,包含从提取中检索到的信息。

    在您的情况下,您只需检查数组的长度。

    您可以在sandbox 上查看示例。

    【讨论】:

    • 通过 a) 添加代码来说明和 b) 添加 Promise.allSettled 是 esnext 功能的警告将大大改善此答案。
    猜你喜欢
    • 1970-01-01
    • 2017-02-15
    • 2021-02-15
    • 2020-04-29
    • 1970-01-01
    • 1970-01-01
    • 2019-12-31
    • 2014-07-30
    • 1970-01-01
    相关资源
    最近更新 更多