【问题标题】:Recursion with an API, using Vanilla JS使用 Vanilla JS 使用 API 进行递归
【发布时间】:2020-08-19 01:24:12
【问题描述】:

我正在玩 Rick and Morty API,我想获取宇宙中的所有角色 到一个数组中,这样我就不必进行更多的 API 调用来处理我的其余代码。

端点https://rickandmortyapi.com/api/character/以页面的形式返回结果,所以 我必须使用递归在一次 API 调用中获取所有数据。

我可以让它将结果输出到 HTML,但我似乎无法获得完整的 JSON 对象数组。

我使用了一些来自 Axios recursion for paginating an api with a cursor

我为我的问题翻译了这个概念,并将其发布在我的Codepen 这是代码:

async function populatePeople(info, universePeople){ // Retrieve the data from the API
  let allPeople = []
  let check = ''
  try {
        return await axios.get(info)
            .then((res)=>{
                // here the current page results is in res.data.results
                for (let i=0; i < res.data.results.length; i++){
                    item.textContent = JSON.stringify(res.data.results[i])
                    allPeople.push(res.data.results[i])
                }

                if (res.data.info.next){
          check = res.data.info.next
                return allPeople.push(populatePeople(res.data.info.next, allPeople))
                }
            })
    } catch (error) {
        console.log(`Error: ${error}`)
    } finally {
    return allPeople
    }
}

populatePeople(allCharacters)
  .then(data => console.log(`Final data length: ${data.length}`))

一些敏锐的眼睛和大脑会有所帮助。 这可能是一件非常简单的事情,我只是想念它。

【问题讨论】:

  • 请重新格式化您的代码,使其更具可读性。另外,请将问题简化为基本部分。我们不是来为您编写代码的。我建议您尝试使其在没有递归的情况下工作。只要它在没有递归的情况下工作,您就可以重构代码以使用递归。

标签: javascript api recursion


【解决方案1】:

以下行有问题:

return allPeople.push(populatePeople(res.data.info.next, allPeople))

这里你将一个 promise 对象推入 allPeople,当 .push() 返回一个数字时,你返回的是一个数字,而不是 allPeople

使用for 循环到push 单个项目从一个数组到另一个实际上是复制数组的一种冗长方式。只有 HTML 部分需要循环。

另外,您将.then()await 混合在一起,这使事情变得复杂。只使用await。使用await 时,不再需要递归。只需将if 替换为循环即可:

while (info) {
   ....
   info = res.data.info.next;
}

您永远不会为universePeople 分配任何东西。你可以去掉这个参数。

您可以使用for...of 语法来代替普通的for 循环。

res 开始,您只使用data 属性,仅使用该属性的变量。

所以把所有这些放在一起,你会得到这个:

async function populatePeople(info) {
    let allPeople = [];
    try {
        while (info) {
            let {data} = await axios.get(info);
            for (let content of data.results) {
                const item = document.createElement('li');
                item.textContent = JSON.stringify(content);
                denizens.append(item);
            }
            allPeople.push(...data.results);
            info = data.info.next;
        }
    } catch (error) {
        console.log(`Error: ${error}`)
    } finally {
        section.append(denizens);
        return allPeople;
    }
}

【讨论】:

  • 感谢您详尽且非常礼貌的代码审查。
  • 感谢@trincot 向我们展示了如何审查代码,我必须学习这种级别的解释:)
  • 一点也不傻。有时候,当你问问题和写代码时,你已经精神疲惫了。因此,任何处于这种情况的人都了解这种情况。这就是为什么会有 StackOverflow。不幸的是,有时人们在这里是如此刻薄,但正如您所知,更多的知识 = 更谦虚。
  • 是的。感谢@ubeyt-demir 的时间。绝对要向那些慷慨分享时间的人学习。我会尽我所能支付。
  • 不解构你可以做let data = (await axios.get(info)).data;
【解决方案2】:

这是递归函数的工作示例

async function getAllCharectersRecursively(URL,results){
    try{
        const {data} =  await axios.get(URL);
        //  concat current  page results
        results =results.concat(data.results)
        if(data.info.next){
            // if there is next page call recursively
            return await getAllCharectersRecursively(data.info.next,results)
        }
        else{
            // at last page there is no next page so return collected results
            return results
        }
    }
    catch(e){
        console.log(e)
    }
}

async function main(){
    let results = await getAllCharectersRecursively("https://rickandmortyapi.com/api/character/",[])
    console.log(results.length)
}
main()

【讨论】:

  • 这是一个很好的答案。 @trincot 用我自己的代码提供了解释和重构,所以我将接受他的回答。
【解决方案3】:

我不愿提供其他答案,因为 Trincot 的分析和答案很准确。

但我认为这里的递归答案可能非常优雅。由于该问题被标记为“递归”,因此似乎值得提出。

const populatePeople = async (url) => {
  const {info: {next}, results} = await axios .get (url)
  return [...results, ...(next ? await populatePeople (next) : [])]
}

populatePeople ('https://rickandmortyapi.com/api/character/')
  // or wrap in an `async` main, or wait for global async...
  .then (people => console .log (people .map (p => p .name)))
  .catch (console .warn)
.as-console-wrapper {max-height: 100% !important; top: 0}
&lt;script&gt;/* dummy */ const axios = {get: (url) =&gt; fetch (url) .then (r =&gt; r .json ())} &lt;/script&gt;

这仅与获取数据有关。将它添加到您的 DOM 应该是一个单独的步骤,而且应该不难。

更新:说明

评论表明这很难解析。我认为这里有两件事可能会很棘手:

  • 首先是{info: {next}, results} = &lt;...&gt; 中的object destructuring。这只是避免使用中间变量来计算我们实际想要使用的变量的好方法。

  • 第二个是return [...results, ...&lt;more&gt;] 中的spread syntax。这是一种比使用.concat.push 更简单的构建数组的方法。 (对象也有类似的功能。)

这是另一个版本做同样的事情,但有一些中间变量和数组连接。它做同样的事情:

const populatePeople = async (url) => {
  const response = await axios .get (url)
  const next = response .info && response .info .next
  const results = response .results || []
  const subsequents = next ? await populatePeople (next) : []
  return results .concat (subsequents)
}

我更喜欢原版。但也许你会发现这一点更清楚。

【讨论】:

  • 哇。相当锋利。我需要一些时间来解析它。
  • @KenIngram:添加了解释。希望对您有所帮助。
  • 这需要我花一点时间来解析,因为我没有很多项目,所以大部分语法都是新的,我必须查一下。总体而言,它似乎强大而简短。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 2011-06-04
  • 2018-11-02
  • 2015-02-02
  • 2012-07-08
  • 1970-01-01
相关资源
最近更新 更多