VanillaJSを使用したAPIによる再帰
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}`))
いくつかの鋭い目と脳が役立つでしょう。それはおそらく本当に単純なものであり、私はそれを見逃しています。
回答
次の行には問題があります。
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;
}
}
これが再帰関数の実例です
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の分析と回答は的確であるため、別の回答を提供することを躊躇します。
しかし、ここでの再帰的な答えは非常にエレガントなものになると思います。そして、質問は「再帰」でタグ付けされているので、提示する価値があるようです。
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)
}
元のバージョンが好きです。しかし、おそらくこれはもっと明確だと思うでしょう。