You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

随机名言生成器优化:仅发起一次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处理异步逻辑,解决之前变量未定义的问题
  • 添加了错误捕获,处理请求失败的情况
  • 支持页面加载时预获取数据,让用户第一次点击更快看到结果

为什么你之前的尝试没成功?

  1. abort()方法用错了:你在xhr.send()之后立刻调用abort,直接终止了请求,根本拿不到数据。abort是用来在请求还没完成时取消(比如用户快速点击多次),不是用来阻止重复请求的。
  2. Fetch的异步逻辑没处理好:你把window.addEventListener('load', fetchApi)的返回值赋值给quotes,但事件监听返回的是undefined,而且fetchApi里的fetch是异步的,返回的是Promise,必须用.then()或者await才能拿到实际的数据。

内容的提问来源于stack exchange,提问作者EhtU

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 11:12:40