JavaScript Fetch如何筛选PokeAPI火/水/草属性宝可梦
核心结论
完全可以在拉取全量宝可梦详情前完成筛选,PokéAPI原生提供按属性维度的聚合接口,根本不需要逐个拉取所有宝可梦的详情再过滤。
原实现的效率问题
你当前的代码存在两个明显的性能损耗点:
- 逻辑上先拉取150只宝可梦的索引列表,再逐个发请求拉取每只的详情,总共要产生151次接口请求,其中不属于火/水/草属性的宝可梦详情请求全是冗余的
- 写法上用
for...of搭配await串行发送请求,整体加载速度非常慢
优化实现方案
PokéAPI自带属性分类接口,火、水、草三个目标属性对应的接口路径分别为:
- 火属性:
/type/fire - 水属性:
/type/water - 草属性:
/type/grass
这三个接口返回数据的pokemon字段,直接列出了所有归属该属性的宝可梦,且每个条目自带slot字段,直接标记当前属性属于宝可梦的第几个属性位,完全满足你检查slot1、slot2属性的需求。
你只需要并发请求这三个接口,合并结果后去重,再按你需要的ID范围(比如前150号关都宝可梦)过滤即可,全程仅需3次接口请求,不需要拉取任何单只宝可梦的详情就能拿到符合要求的宝可梦列表。如果你确实需要宝可梦的完整详情,也只需要对筛选后的小部分结果发详情请求,请求量会从150次降到几十次甚至更少。
参考代码
const TARGET_TYPES = ['fire', 'water', 'grass'] const MAX_POKEMON_ID = 150 // 限定取前150号全国图鉴宝可梦 const TYPE_API_BASE = 'https://pokeapi.co/api/v2/type/' async function getFilteredPokemons() { try { // 并发拉取三个属性的关联宝可梦列表 const typeResults = await Promise.all( TARGET_TYPES.map(type => fetch(`${TYPE_API_BASE}${type}`).then(res => res.json())) ) // 合并结果、去重、过滤ID范围 const pokemonCache = new Map() typeResults.forEach(typeData => { typeData.pokemon.forEach(relation => { // 只保留slot1、slot2的属性关联 if (relation.slot > 2) return const pokemonUrl = relation.pokemon.url // 从URL中提取宝可梦全国图鉴ID const pokemonId = Number(pokemonUrl.split('/').filter(Boolean).pop()) // 过滤超出ID范围的宝可梦,同时去重 if (pokemonId <= MAX_POKEMON_ID && !pokemonCache.has(pokemonId)) { pokemonCache.set(pokemonId, { id: pokemonId, name: relation.pokemon.name, url: pokemonUrl }) } }) }) // 如果需要完整详情,取消下面这段注释即可,只会请求筛选后的宝可梦 // const finalList = await Promise.all( // Array.from(pokemonCache.values()).map(pokemon => // fetch(pokemon.url).then(res => res.json()) // ) // ) // 不需要详情直接用缓存的列表即可 const finalList = Array.from(pokemonCache.values()) console.log(finalList) return finalList } catch (err) { console.error('拉取数据失败:', err) } } getFilteredPokemons()
补充提示:如果你只需要宝可梦的名称、详情页地址这类基础信息,直接用三个属性接口返回的结果即可,不需要额外发任何详情请求,加载速度比你原来的实现快几十倍。
内容的提问来源于stack exchange,提问作者Alejandro Arias Aguilar
相关产品推荐
相关产品推荐

