使用原生JS实现HN克隆时如何进行嵌套AJAX请求?
如何在Hacker News克隆项目中实现嵌套AJAX调用获取新闻详情?
看起来你已经走对路啦——你已经成功拿到了Hacker News Top Stories的ID列表,接下来只需要针对每个ID发起嵌套的AJAX请求,就能拿到每条新闻的详细数据了。我来给你拆解下具体怎么实现,还有一些优化点要注意:
基于原生XMLHttpRequest的实现方案
先顺着你现有的代码逻辑修改,核心是在第一个请求的回调里,为每个新闻ID创建新的AJAX请求:
var apiResult = document.getElementById("apiResult"); function apiContent() { var url_end = 'https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty'; var apiCall = new XMLHttpRequest(); apiCall.onload = function() { if(this.status == 200) { var storyIds = JSON.parse(apiCall.responseText); // 先取前20条测试,避免一次性发起500个请求导致性能问题 var topStoryIds = storyIds.slice(0, 20); topStoryIds.forEach(function(storyId) { // 为每个新闻ID创建新的AJAX实例 var storyRequest = new XMLHttpRequest(); var storyUrl = `https://hacker-news.firebaseio.com/v0/item/${storyId}.json?print=pretty`; storyRequest.onload = function() { if(this.status === 200) { var storyData = JSON.parse(this.responseText); // 把新闻数据渲染到页面 renderStory(storyData); } else { console.error(`获取ID为${storyId}的新闻失败:${this.status}`); } }; // 处理请求错误 storyRequest.onerror = function() { console.error(`请求ID为${storyId}的新闻时发生错误`); }; storyRequest.open('GET', storyUrl, true); storyRequest.send(); }); } else { console.error(`获取Top Stories列表失败:${this.status}`); } }; apiCall.onerror = function() { console.error("请求Top Stories列表时发生错误"); }; apiCall.open('GET', url_end, true); apiCall.send(); } // 辅助函数:渲染单条新闻到页面 function renderStory(story) { var storyElement = document.createElement('div'); storyElement.innerHTML = ` <h3><a href="${story.url}" target="_blank">${story.title}</a></h3> <p>作者:${story.by} | 评分:${story.score} | 评论数:${story.descendants || 0}</p> <hr> `; apiResult.appendChild(storyElement); } // 启动数据获取 apiContent();
关键注意点:
- 嵌套回调的时机:必须在第一个请求的
onload回调里发起后续请求,确保已经拿到了新闻ID列表 - 错误处理:给每个请求都加上错误捕获,避免单个请求失败导致整个流程卡住
- 性能控制:HN的Top Stories有500个ID,一次性发起所有请求会很慢甚至被限流,建议先取前20条测试
更简洁的现代方案:使用Fetch + Async/Await
如果你想避免“回调地狱”,可以用ES6+的fetchAPI和async/await语法,让异步代码更易读:
var apiResult = document.getElementById("apiResult"); async function apiContent() { try { // 第一步:获取Top Stories的ID列表 const topStoriesResponse = await fetch('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty'); if (!topStoriesResponse.ok) throw new Error(`获取Top Stories失败:${topStoriesResponse.status}`); const storyIds = await topStoriesResponse.json(); const topStoryIds = storyIds.slice(0, 20); // 第二步:并行发起所有新闻详情请求 const storyPromises = topStoryIds.map(storyId => fetch(`https://hacker-news.firebaseio.com/v0/item/${storyId}.json?print=pretty`) .then(res => { if (!res.ok) throw new Error(`获取ID为${storyId}的新闻失败:${res.status}`); return res.json(); }) ); // 等待所有请求完成 const stories = await Promise.all(storyPromises); // 批量渲染新闻 stories.forEach(story => renderStory(story)); } catch (error) { console.error(error.message); } } function renderStory(story) { var storyElement = document.createElement('div'); storyElement.innerHTML = ` <h3><a href="${story.url}" target="_blank">${story.title}</a></h3> <p>作者:${story.by} | 评分:${story.score} | 评论数:${story.descendants || 0}</p> <hr> `; apiResult.appendChild(storyElement); } apiContent();
这个方案的优势:
async/await让异步代码看起来像同步逻辑,可读性大幅提升Promise.all可以并行处理所有新闻请求,比逐个发起请求更快- 统一的
try/catch块处理所有错误,代码更简洁
内容的提问来源于stack exchange,提问作者alittlecurryhot
相关产品推荐
相关产品推荐

