如何使用两个 map 函数进行两次提取 url

How to use two map function with two fetching url

我有两个 api 我应该把它们变成这样的数组

[
            {
                key: "0",
                label: 'Installation',
                children: [
                    { key: "0-0", label: 'Getting Started', url: 'https://reactjs.org/docs/getting-started.html' },
                    { key: "0-1", label: 'Add React', url: 'https://reactjs.org/docs/add-react-to-a-website.html' },
                    { key: "0-2", label: 'Create an App', url: 'https://reactjs.org/docs/create-a-new-react-app.html' },
                    { key: "0-3", label: 'CDN Links', url: 'https://reactjs.org/docs/cdn-links.html' }
                ]
            },
            {
                key: "1",
                label: 'Main Concepts',
                children: [
                    { key: "1-0", label: 'Hello World', url: 'https://reactjs.org/docs/hello-world.html' },
                    { key: "1-1", label: 'Introducing JSX', url: 'https://reactjs.org/docs/introducing-jsx.html' },
                    { key: "1-2", label: 'Rendering Elements', url: 'https://reactjs.org/docs/rendering-elements.html' },
                    { key: "1-3", label: 'Components and Props', url: 'https://reactjs.org/docs/components-and-props.html' },
                    { key: "1-4", label: 'State and LifeCycle', url: 'https://reactjs.org/docs/state-and-lifecycle.html' },
                    { key: "1-5", label: 'Handling Events', url: 'https://reactjs.org/docs/handling-events.html' }
                ]
            }
        ];

顺便说一句,我有两个API,其中一个获取所有类别,第二个API 是获取children 的名称。所以我创建了链式提取,但我无法正确获取 children object。 children 数组转 'children: Promise {: Array(11)}' fullfilled 这里是什么意思?因为我的代码由于这个原因无法正常工作。 我也想分享我的代码

 getCategories() {
    return fetch('https://challenge.fnaghshin.com/api/cat')
      .then((res) => res.json())
      .then((d) => {
        return Promise.all(
          d.map((e) => {
            return {
              key: e.id,
              label: e.name,
              date: e.created_at,
              children: fetch(
                `https://challenge.fnaghshin.com/api/catpro/${e.id}`
              )
                .then((res) => res.json())
                .then((d) =>
                  d.map((e) => {
                    return {
                      key: e.id,
                      label: e.name,
                    };
                  })
                ),
            };
          })
        );
      });
  }

我还用 Promise.all() 来等待所有的承诺都实现而不是让我正确 array.But 我失败了

IMO 最干净的解决方案是使用 async/await,使用嵌套的 Promises 它很快变得相当不可读。

async function getCategories() {
  const response = await fetch("https://challenge.fnaghshin.com/api/cat");
  const categories = await response.json();

  const childrenResponses = await Promise.all(
    categories.map((cat) =>
      fetch(`https://challenge.fnaghshin.com/api/catpro/${cat.id}`)
    )
  );

  const childrenData = await Promise.all(
    childrenResponses.map((response) => response.json())
  );

  return categories.map((cat, index) => ({
    key: cat.id,
    label: cat.name,
    date: cat.created_at,
    children: childrenData[index].map((child) => ({
      key: child.id,
      label: child.title
    }))
  }));
}

所以首先你得到你的类别,这很简单。然后,您要做的是在具有您从类别 (childrenResponses) 中获得的 ID 的提取调用列表中使用 Promise.all()。然后你必须做同样的事情,因为 .json() 在 fetch 中(它会更干净,例如 axios 或类似的更高级别的库而不是 fetch),一旦你有类别和 childrenData你只需要按照你想要的方式映射它。你可以通过做例如让它更简洁一点改为:

const categories = await fetch(
  "https://challenge.fnaghshin.com/api/cat"
).then((res) => res.json());

尽管在我看来它的可读性较差。您可能还应该做一些错误处理,例如try/catch.

Promise.all() 需要一组 promise,但您 return 是一组对象。 promise 嵌套在对象的 children 属性中。所以我们需要做的是映射这些对象和 return 嵌套的 prop children 并将其提供给 Promise.all()

我在第二个 then 块中添加了一个异步函数,因此我可以将 awaitPromise.all() 一起使用。这导致代码的执行要等到所有承诺,即所有 children 都已解决。之后我可以简单地 return 结果。

function getCategories() {
  return fetch('https://challenge.fnaghshin.com/api/cat')
    .then((res) => res.json())
    .then(async (d) => {
      const withChildren = d.map((e) => {
        return {
          key: e.id,
          label: e.name,
          date: e.created_at,
          children: fetch(`https://challenge.fnaghshin.com/api/catpro/${e.id}`).then(res => res.json())
        }
      })
      await Promise.all(withChildren.map(item => item.children))
      return withChildren
    })
}

429 (Too Many Requests) 似乎是一个速率限制,当您在特定时间内过于频繁地触发请求时。我也有。现在又没了。

PS:在这种情况下不要在map()中使用await,因为这会使执行持续时间更长。因为在地图的每次迭代中,它都会 await fetch() 的响应,然后再进行下一次迭代并触发下一个 fetch()。因此,不是一次(基本上)解雇他们,而是一个接一个解雇他们。一次触发它们,然后等待 Promise.all().

会更快

这是基于承诺的版本。我把主要功能分成几个部分,这样更容易阅读。

请注意,因为您正在向 API 发出许多请求,所以您可能会收到 429: too many requests 警告。

要解决这个问题,您可以为每个请求使用代理,或者您可以检查状态代码并等待响应告诉您等待的时间。

const URL = 'https://challenge.fnaghshin.com/api/cat';
const CHILDREN_URL = `https://challenge.fnaghshin.com/api/catpro/`;

function getCategories() {
  return fetch(URL)
    .then(res => res.json())
    .then(data => Promise.all(data.map(getSubCategories)));
}

function getSubCategories(data) {
  return fetch(CHILDREN_URL + data.id)
    .then(res => res.json())
    .then(info => ({
      key: data.id,
      label: data.name,
      date: data.created_at,
      children: info.map(e => ({
        key: e.id,
        label: e.title,
      })),
    }));
}

getCategories()
  .then(console.log)
  .catch(console.log)