随机名言生成器优化:仅发起一次API请求实现本地切换
解决随机名言生成器的重复请求问题:单次API请求复用数据
我来帮你搞定这个问题!你的核心需求是只发起一次API请求,之后复用返回的名言数据,之前的问题主要出在每次点击都重复发起请求,以及Fetch API的异步逻辑处理不当。下面分两种方案给出完整的实现:
一、XMLHttpRequest 版本(修复重复请求)
核心思路是用一个全局变量缓存已获取的名言数据,点击按钮时先检查缓存:有数据就直接展示,没有才发起请求。
修改后的完整代码:
const button = document.querySelector('button'); const innerQuotesDiv = document.querySelectorAll('div')[2]; const article = innerQuotesDiv.firstElementChild; const authorName = article.nextElementSibling; let cachedQuotes = null; // 用来缓存名言数据的全局变量 function generateRandomIndex(length) { return Math.floor(Math.random() * length); } function getQuotes() { // 先检查缓存,有数据直接展示,不用再发请求 if (cachedQuotes) { displayQuotes(cachedQuotes); return; } let xhr = new XMLHttpRequest(); xhr.open('GET', 'https://type.fit/api/quotes'); xhr.onload = function() { if (xhr.status === 200) { cachedQuotes = JSON.parse(xhr.response); displayQuotes(cachedQuotes); } else { alert('请求失败,状态码:' + xhr.status); } }; xhr.onerror = function() { alert('网络出错啦,请检查连接'); }; xhr.send(); } function displayQuotes(quotes) { const index = generateRandomIndex(quotes.length); innerQuotesDiv.classList.add('inner'); article.textContent = quotes[index].text; // 简化判断:如果作者为空就显示Anonymous authorName.textContent = quotes[index].author || 'Anonymous'; } button.addEventListener('click', getQuotes);
关键改进:
- 新增
cachedQuotes变量存储已获取的数据,彻底避免重复请求 - 把展示逻辑抽离到
displayQuotes函数,代码更清晰复用性更强 - 完善了请求失败的状态判断和错误提示
二、Fetch API 版本(解决异步变量未定义问题)
Fetch是异步的,你之前的问题是直接把事件监听的返回值赋值给quotes,但那不是实际的数据,而是undefined。我们可以用Promise和async/await来正确处理:
修改后的完整代码:
const button = document.querySelector('button'); const innerQuotesDiv = document.querySelectorAll('div')[2]; const article = innerQuotesDiv.firstElementChild; const authorName = article.nextElementSibling; let cachedQuotes = null; let quotesPromise = null; // 存储请求的Promise,防止同时发起多个请求 function generateRandomIndex(length) { return Math.floor(Math.random() * length); } function fetchQuotes() { // 如果已有请求在进行,直接返回那个Promise,不重复发请求 if (quotesPromise) { return quotesPromise; } quotesPromise = fetch('https://type.fit/api/quotes') .then(response => { if (!response.ok) { throw new Error('请求失败:' + response.status); } return response.json(); }) .then(data => { cachedQuotes = data; return data; }) .catch(error => { alert(error.message); quotesPromise = null; // 请求失败后重置,允许用户再次尝试 throw error; }); return quotesPromise; } function displayQuotes(quotes) { const index = generateRandomIndex(quotes.length); innerQuotesDiv.classList.add('inner'); article.textContent = quotes[index].text; authorName.textContent = quotes[index].author || 'Anonymous'; } button.addEventListener('click', async () => { try { // 优先用缓存,没有就等待请求完成 const quotes = cachedQuotes || await fetchQuotes(); displayQuotes(quotes); } catch (error) { console.error('处理出错:', error); } }); // 可选:页面加载时提前获取数据,提升用户体验 // window.addEventListener('load', fetchQuotes);
关键改进:
- 用
quotesPromise存储请求的Promise,避免同时发起多个请求 - 用
async/await处理异步逻辑,解决之前变量未定义的问题 - 添加了错误捕获,处理请求失败的情况
- 支持页面加载时预获取数据,让用户第一次点击更快看到结果
为什么你之前的尝试没成功?
- abort()方法用错了:你在
xhr.send()之后立刻调用abort,直接终止了请求,根本拿不到数据。abort是用来在请求还没完成时取消(比如用户快速点击多次),不是用来阻止重复请求的。 - Fetch的异步逻辑没处理好:你把
window.addEventListener('load', fetchApi)的返回值赋值给quotes,但事件监听返回的是undefined,而且fetchApi里的fetch是异步的,返回的是Promise,必须用.then()或者await才能拿到实际的数据。
内容的提问来源于stack exchange,提问作者EhtU
相关产品推荐
相关产品推荐

