JavaScript AbortController

摘要:在本教程中,您将探索如何使用 JavaScript AbortController 来取消正在进行的网络请求。

JavaScript AbortController 简介

AbortController 是一个 Web API,允许您中止网络请求。它通过将一个 AbortSignal 与网络请求关联起来。该信号可用于与网络请求通信,以便在必要时中止它们。

以下是使用 AbortController 中止 fetch 请求的步骤

首先,使用 new 关键字创建一个 AbortController 对象

const controller = new AbortController();Code language: JavaScript (javascript)

其次,获取 AbortController 对象的 signal 属性。signal 属性是 AbortSignal 对象的一个实例

const signal = controller.signal;Code language: JavaScript (javascript)

signal 可以传递给 fetch 请求,使我们能够控制其行为。

第三,将 AbortSignal 对象传递给 fetch 方法

fetch(url, { signal });Code language: JavaScript (javascript)

或者,您可以将 signal 传递给 Request 对象并在 fetch 方法中使用它

const request = new Request(url, { signal });
fetch(request);Code language: JavaScript (javascript)

第四,如果需要,通过调用 AbortController 对象的 abort() 方法来中止 fetch 请求。

 controller.abort();Code language: JavaScript (javascript)

例如,您在 2 秒后使 fetch 超时

setTimeout(() => controller.abort(), 2000);Code language: JavaScript (javascript)

如果您在 fetch 完成后调用 abort(),则没关系,fetch 将忽略它。

第五,当您调用 abort() 方法时,它会通知 abort 信号。您可以使用 signal 对象上的 addEventListener 监听 abort 事件

signal.addEventListener('abort', () => {
    console.log(signal.aborted); // true
});Code language: JavaScript (javascript)

最后,您可以对中止的 fetch 做出反应

fetch(url, { signal }).then(response => {
    return response.json();
}).then(data => {
    console.log(data);
}).catch(err => {
    if (err.name === 'AbortError') {
       console.log('Fetch aborted');
    }
});Code language: JavaScript (javascript)

请注意,AbortError 不是真正的错误,因此我们使用 if 语句专门处理 AbortError

JavaScript AbortController 示例

在实践中,您在处理用户交互时使用 AbortController。例如,在搜索页面上,当用户搜索新搜索词时,您可以中止先前的请求。

我们将构建一个 在维基百科上搜索词条的应用程序,如果用户在上次搜索完成之前开始新的搜索,则会取消先前的搜索请求。

步骤 1. 创建一个新目录来存储项目文件。

mkdir wikipedia-search-abortableCode language: JavaScript (javascript)

步骤 2. 在项目目录中创建以下目录和文件

wikipedia-search-abortable
├── css
|  └── style.css
├── img
|  ├── spinner.svg
|  └── wikipedia-logo.png
├── index.html
└── js
   └── app.jsCode language: JavaScript (javascript)

步骤 3. 在 index.html 文件中创建 HTML 结构

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Wikipedia Search</title>
        <script src="./js/app.js" defer></script>
        <link rel="stylesheet" href="./css/style.css">
    </head>

    <body>
        <header>
            <img src="./img/wikipedia-logo.png" alt="wikipedia">
            <h1>Wikipedia Search</h1>
            <form id="search">
                <input type="search" name="term" id="term" placeholder="Enter a search term...">
            </form>
        </header>

        <main>
            <div id="searchResult"></div>
            <div id="loading"></div>
            <div id="error"></div>
        </main>

    </body>

</html>Code language: HTML, XML (xml)

步骤 4. 使用以下代码修改 app.js 文件

const form = document.querySelector('#search');
const termInput = document.querySelector('#term');
const error = document.querySelector('#error');
const loading = document.querySelector('#loading');
const resultsContainer = document.querySelector('#searchResult');

let controller = new AbortController();

const search = async (term) => {
  // Abort the previous request
  console.log('Abort the previous request');
  controller.abort();

  // Create a new controller for the new request
  controller = new AbortController();
  const signal = controller.signal;
  const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srsearch=${term}`;

  const response = await fetch(url, { signal });
  const data = await response.json();
  return data.query.search;
};

const resetSearchResult = () => {
  error.innerHTML = '';
  loading.innerHTML = '';
  resultsContainer.innerHTML = '';
};

const handleSubmit = async (e) => {
  e.preventDefault();

  // reset search result
  resetSearchResult();

  // check if term is empty
  const term = termInput.value;
  if (term === '') return;

  try {
    // show the loading
    loading.innerHTML = `<img src='./img/spinner.svg' alt='loading...'>`;

    // make the search
    const results = await search(term);

    // show the search result
    resultsContainer.innerHTML = renderSearchResult(results);
  } catch (err) {
    // show the error
    error.innerHTML = `Something went wrong.`;
    console.error(err);
  } finally {
    // hide the loading
    loading.innerHTML = '';
  }
};

const renderSearchResult = (results) => {
  return results
    .map(({ title, snippet, pageid }) => {
      return `<article>
                <a href="https://en.wikipedia.org/?curid=${pageid}">
                    <h2>${title}</h2>
                </a>
                <div class="summary">${snippet}...</div>
            </article>`;
    })
    .join('');
};

form.addEventListener('submit', handleSubmit);Code language: JavaScript (javascript)

它是如何工作的。

首先,使用 querySelector 方法选择 DOM 元素

const form = document.querySelector('#search');
const termInput = document.querySelector('#term');
const error = document.querySelector('#error');
const loading = document.querySelector('#loading');
const resultsContainer = document.querySelector('#searchResult');Code language: JavaScript (javascript)

其次,创建一个 AbortController 来中止 fetch 请求

let controller = new AbortController();Code language: JavaScript (javascript)

第三,定义一个搜索函数,该函数根据输入的搜索词调用维基百科 API

const search = async (term) => {
  // Abort the previous request
  console.log('Abort the previous request');
  controller.abort();

  // Create a new controller for the new request
  controller = new AbortController();
  const signal = controller.signal;
  const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srsearch=${term}`;
  const response = await fetch(url, { signal });
  const data = await response.json();
  return data.query.search;
};Code language: JavaScript (javascript)

search() 函数中,如果用户开始新的搜索,我们将调用 AbortController 对象的 abort() 方法来取消先前的搜索请求

controller.abort();Code language: JavaScript (javascript)

在发出新请求之前,我们创建一个新的 AbortController 对象,并将它的 AbortSignal 传递给 fetch() 方法

controller = new AbortController();
const signal = controller.signal;
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srsearch=${term}`;
const response = await fetch(url, { signal });Code language: JavaScript (javascript)

步骤 5. 定义一个函数来重置错误、加载和搜索结果

const resetSearchResult = () => {
  error.innerHTML = '';
  loading.innerHTML = '';
  resultsContainer.innerHTML = '';
};Code language: JavaScript (javascript)

步骤 6. 定义一个 handleSubmit() 函数,在提交表单时执行搜索

const handleSubmit = async (e) => {
  e.preventDefault();

  // reset search result
  resetSearchResult();

  // check if term is empty
  const term = termInput.value;
  if (term === '') return;

  try {
    // show the loading
    loading.innerHTML = `<img src='./img/spinner.svg' alt='loading...'>`;

    // make the search
    const results = await search(term);

    // show the search result
    resultsContainer.innerHTML = renderSearchResult(results);
  } catch (err) {
    // show the error
    error.innerHTML = `Something went wrong.`;
    console.error(err);
  } finally {
    // hide the loading
    loading.innerHTML = '';
  }
};Code language: JavaScript (javascript)

它是如何工作的。

首先,阻止表单提交到服务器

e.preventDefault();Code language: JavaScript (javascript)

其次,调用 resetSearchResult 函数来重置搜索结果

resetSearchResult();Code language: JavaScript (javascript)

第三,如果搜索词为空,则立即返回

const term = termInput.value;
if (term === '') return;Code language: JavaScript (javascript)

第四,在发出搜索请求之前显示加载进度指示器

loading.innerHTML = `<img src='./img/spinner.svg' alt='loading...'>`;Code language: JavaScript (javascript)

第五,发出搜索请求

const results = await search(term);Code language: JavaScript (javascript)

第六,显示搜索结果

resultsContainer.innerHTML = renderSearchResult(results);Code language: JavaScript (javascript)

第七,显示用户友好的用户消息,并在控制台中记录错误详细信息

 } catch (err) {
    // show the error
    error.innerHTML = `Something went wrong.`;
    console.error(err);
 }Code language: JavaScript (javascript)

第八,无论是否发生错误,都隐藏加载指示器

} finally {
  // hide the loading
  loading.innerHTML = '';
}Code language: JavaScript (javascript)

第九,定义一个函数 renderSearchResult 来渲染搜索结果

const renderSearchResult = (results) => {
  return results
    .map(({ title, snippet, pageid }) => {
      return `<article>
                <a href="https://en.wikipedia.org/?curid=${pageid}">
                    <h2>${title}</h2>
                </a>
                <div class="summary">${snippet}...</div>
            </article>`;
    })
    .join('');
};Code language: JavaScript (javascript)

最后,将 handleSubmit 函数注册为表单的提交事件处理程序

form.addEventListener('submit', handleSubmit);Code language: JavaScript (javascript)

下载项目源代码

下载项目源代码

总结

  • 使用 AbortController 中止 Web 请求,以防止不必要的网络请求并有效地处理用户交互。
本教程对您有帮助吗?