VanillaJSを使用したAPIによる再帰

Aug 18 2020

Rick and Morty APIで遊んでいて、ユニバースのすべての文字を配列に入れたいので、残りのコードを機能させるためにAPI呼び出しを追加する必要はありません。

エンドポイントhttps://rickandmortyapi.com/api/character/は結果をページで返すため、再帰を使用して1回のAPI呼び出しですべてのデータを取得する必要があります。

結果をHTMLに吐き出すことはできますが、JSONオブジェクトの完全な配列を取得できないようです。

カーソルでAPIをページ付けするためにAxios再帰からのいくつかのアイデアを使用しています

問題の概念を翻訳し、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}`))

いくつかの鋭い目と脳が役立つでしょう。それはおそらく本当に単純なものであり、私はそれを見逃しています。

回答

3 trincot Aug 18 2020 at 13:34

次の行には問題があります。

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ループの代わりに、for...of構文を使用できます。

以下からのよう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;
    }
}
3 UbeytDemir Aug 18 2020 at 13:17

これが再帰関数の実例です

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()
2 ScottSauyet Aug 18 2020 at 20:03

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}
<script>/* dummy */ const axios = {get: (url) => fetch (url) .then (r => r .json ())} </script>

これは、データのフェッチにのみ関係します。それをDOMに追加することは別のステップであり、難しいことではありません。

更新:説明

コメントは、これを解析するのが難しいことを示しました。ここで注意が必要なことが2つあります。

  • 1つ目は、でのオブジェクトの破壊です{info: {next}, results} = <...>。これは、実際に使用したい変数を計算するために中間変数を使用しないようにするための優れた方法です。

  • 2つ目は、のスプレッド構文ですreturn [...results, ...<more>]。これは、.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)
}

元のバージョンが好きです。しかし、おそらくこれはもっと明確だと思うでしょう。