{"id":35539625,"url":"https://github.com/tensiile/saborter","last_synced_at":"2026-02-04T21:02:38.016Z","repository":{"id":332408754,"uuid":"1124291900","full_name":"TENSIILE/saborter","owner":"TENSIILE","description":"🚀 📩 A simple and efficient library for canceling asynchronous requests using AbortController","archived":false,"fork":false,"pushed_at":"2026-01-27T21:30:30.000Z","size":622,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"develop","last_synced_at":"2026-01-28T04:44:15.101Z","etag":null,"topics":["abort","abortcontroller","abortsignal","javascript","javascript-library","simple","ts","typescript-library","zero-dependency"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TENSIILE.png","metadata":{"files":{"readme":"readme.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-28T18:29:56.000Z","updated_at":"2026-01-27T21:30:31.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/TENSIILE/saborter","commit_stats":null,"previous_names":["tensiile/saborter"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/TENSIILE/saborter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TENSIILE%2Fsaborter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TENSIILE%2Fsaborter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TENSIILE%2Fsaborter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TENSIILE%2Fsaborter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TENSIILE","download_url":"https://codeload.github.com/TENSIILE/saborter/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TENSIILE%2Fsaborter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29096329,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-04T21:00:18.276Z","status":"ssl_error","status_checked_at":"2026-02-04T20:59:11.235Z","response_time":62,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abort","abortcontroller","abortsignal","javascript","javascript-library","simple","ts","typescript-library","zero-dependency"],"created_at":"2026-01-04T04:13:09.547Z","updated_at":"2026-02-04T21:02:38.009Z","avatar_url":"https://github.com/TENSIILE.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Logo](./assets/logo.png)\n\n[![Npm package](https://img.shields.io/npm/v/saborter?color=red\u0026label=npm%20package)](https://www.npmjs.com/package/saborter)\n![Static Badge](https://img.shields.io/badge/coverage-90%25-orange)\n![Static Badge](https://img.shields.io/badge/license-MIT-blue)\n[![Github](https://img.shields.io/badge/repository-github-color)](https://github.com/TENSIILE/saborter)\n\nA simple and effective library for canceling asynchronous requests using AbortController.\n\n## 📚 Documentation\n\nThe documentation is divided into several sections:\n\n- [Installation](#📦-installation)\n- [Why Saborter?](#📈-why-saborter)\n- [Quick Start](#🚀-quick-start)\n- [Key Features](#📖-key-features)\n- [API](#🔧-api)\n- [Additional APIs](#🔌-additional-apis)\n- [Important Features](#⚠️-important-features)\n- [Troubleshooting](#🐜-troubleshooting)\n- [Usage Examples](#🎯-usage-examples)\n- [Compatibility](#💻-compatibility)\n- [License](#📋-license)\n\n## 📦 Installation\n\n```bash\nnpm install saborter\n# or\nyarn add saborter\n```\n\n### Related libraries\n\n- [React](https://github.com/TENSIILE/saborter-react) - a standalone library with `Saborter` and `React` integration.\n\n## 📈 Why Saborter ?\n\n| Function/Characteristic                                                                                                               | Saborter | AbortController |\n| ------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------------- |\n| Eliminated race condition when speed typing.                                                                                          | ✔️       | ✖️              |\n| The signal is created anew, there is no need to recreate it yourself. After `abort()` you can \"reset\" and use it again.               | ✔️       | ✖️              |\n| Legible error handling across all browsers.                                                                                           | ✔️       | ✖️              |\n| There is extended information about request interruptions: who cancelled, when, and the reason.                                       | ✔️       | ✖️              |\n| The signal will always be new. It's no coincidence that a previously disabled signal can appear from outside, which breaks all logic. | ✔️       | ✖️              |\n\n## 🚀 Quick Start\n\n### Basic Usage\n\n```javascript\nimport { Aborter } from 'saborter';\n\n// Create an Aborter instance\nconst aborter = new Aborter();\n\n// Use for the request\nconst fetchData = async () =\u003e {\n  try {\n    const result = await aborter.try((signal) =\u003e fetch('/api/data', { signal }));\n    console.log('Data received:', result);\n  } catch (error) {\n    console.error('Request error:', error);\n  }\n};\n```\n\n## 📖 Key Features\n\n### 1. Automatically canceling previous requests\n\nEach time `try()` is called, the previous request is automatically canceled:\n\n```javascript\n// When searching with autocomplete\nconst handleSearch = async (query) =\u003e {\n  // The previous request is automatically canceled\n  const results = await aborter.try((signal) =\u003e fetch(`/api/search?q=${query}`, { signal }));\n  return results;\n};\n\n// When the user quickly types:\nhandleSearch('a'); // Starts\nhandleSearch('ab'); // The first request is canceled, a new one is started\nhandleSearch('abc'); // The second request is canceled, a new one is started\n```\n\n### 2. Automatic cancellation of requests\n\nThe `Aborter` class allows you to easily cancel ongoing requests:\n\n```javascript\nconst aborter = new Aborter();\n\nconst fetcher = (signal) =\u003e fetch('/api/long-task', { signal });\n\n// Start a long-running request and cancel the request after 2 seconds\nconst longRequest = aborter.try(fetcher, { timeout: { ms: 2000 } });\n```\n\n### 3. Working with Multiple Requests\n\nYou can create separate instances for different groups of requests:\n\n```javascript\n// Separate requests by type\nconst userAborter = new Aborter();\nconst dataAborter = new Aborter();\n\n// Manage user requests separately\nconst fetchUser = async (id) =\u003e {\n  return userAborter.try((signal) =\u003e fetch(`/api/users/${id}`, { signal }));\n};\n\n// And manage data separately\nconst fetchData = async (params) =\u003e {\n  return dataAborter.try((signal) =\u003e fetch('/api/data', { signal, ...params }));\n};\n\n// Cancel only user requests\nconst cancelUserRequests = () =\u003e {\n  userAborter.abort();\n};\n```\n\n## 🔧 API\n\n### Constructor\n\n```typescript\nconst aborter = new Aborter(options?: AborterOptions);\n```\n\n### Constructor Parameters\n\n| Parameter | Type             | Description                   | Required |\n| --------- | ---------------- | ----------------------------- | -------- |\n| `options` | `AborterOptions` | Aborter configuration options | No       |\n\n**AborterOptions:**\n\n```typescript\n{\n  /*\n    Callback function for abort events.\n    Associated with EventListener.onabort.\n    It can be overridden via `aborter.listeners.onabort`\n  */\n  onAbort?: OnAbortCallback;\n\n  /*\n    A function called when the request state changes.\n    It takes the new state as an argument.\n    Can be overridden via `aborter.listeners.state.onstatechange`\n  */\n  onStateChange?: OnStateChangeCallback;\n}\n```\n\n### Properties\n\n⚠️ `[DEPRECATED] signal`\n\nReturns the `AbortSignal` associated with the current controller.\n\n\u003e [!WARNING]\n\u003e It's best not to use a signal to subscribe to interrupts or check whether a request has been interrupted.\n\u003e The signal is updated on every attempt, and your subscriptions will be lost, causing a memory leak.\n\n```javascript\nconst aborter = new Aborter();\n\n// Using signal in the request\nfetch('/api/data', {\n  signal: aborter.signal\n});\n```\n\n`isAborted`\n\nReturns a `boolean` value indicating whether the request was aborted or not.\n\n`listeners`\n\nReturns an `EventListener` object to listen for `Aborter` events.\n\n[Detailed documentation here](./docs/event-listener.md)\n\n⚠️ `[DEPRECATED] static errorName`\n\nUse `AbortError.name`.\n\nName of the `AbortError` error instance thrown by AbortSignal.\n\n```javascript\nconst result = await aborter\n  .try((signal) =\u003e fetch('/api/data', { signal }), { isErrorNativeBehavior: true })\n  .catch((error) =\u003e {\n    if (error.name === AbortError.name) {\n      console.log('Canceled');\n    }\n  });\n```\n\n### Methods\n\n`try(request, options?)`\n\nExecutes an asynchronous request with the ability to cancel.\n\n**Parameters:**\n\n- `request: (signal: AbortSignal) =\u003e Promise\u003cT\u003e` - the function that fulfills the request\n- `options?: Object` (optional)\n  - `isErrorNativeBehavior?: boolean` - a flag for controlling error handling. Default is `false`\n  - `timeout?: Object`\n    - `ms: number` - Time in milliseconds after which interrupts should be started\n    - `reason?: any` - A field storing the error reason. Can contain any metadata\n\n**Returns:** `Promise\u003cT\u003e`\n\n**Examples:**\n\n```javascript\n// Simple request\nconst result = await aborter.try((signal) =\u003e {\n  return fetch('/api/data', { signal }).then((response) =\u003e response.json());\n});\n\n// With custom request logic\nconst result = await aborter.try(async (signal) =\u003e {\n  const response = await fetch('/api/data', { signal });\n  if (!response.ok) {\n    throw new Error('Server Error');\n  }\n  return response.json();\n});\n```\n\n**Examples using automatic cancellation after a time:**\n\n```javascript\n// Request with automatic cancellation after 2 seconds\nconst result = await aborter.try(\n  (signal) =\u003e {\n    return fetch('/api/data', { signal });\n  },\n  { timeout: { ms: 2000 } }\n);\n\n// We want to get an error in the \"catch\" block\ntry {\n  const result = await aborter.try(\n    (signal) =\u003e {\n      return fetch('/api/data', { signal });\n    },\n    { timeout: { ms: 2000 } }\n  );\n} catch (error) {\n  if (error instanceof AbortError \u0026\u0026 error.initiator === 'timeout') {\n    // We'll get an AbortError error here with a timeout reason.\n\n    if (error.cause instanceof TimeoutError) {\n      // To get the parameters that caused the timeout error,\n      // they can be found in the \"cause\" field using the upstream typeguard.\n\n      console.log(error.cause.ms); // `error.cause` — TimeoutError\n    }\n  }\n}\n```\n\nIf you want to catch a [timeout error through events or subscriptions](./docs/event-listener.md#catching-an-error-by-timeout), you can do that.\n\n`abort(reason?)`\n\n**Parameters:**\n\n- `reason?: any` - the reason for aborting the request (optional)\n\nImmediately cancels the currently executing request.\n\n**Examples:**\n\n```javascript\n// Start the request\nconst requestPromise = aborter.try((signal) =\u003e fetch('/api/data', { signal }), { isErrorNativeBehavior: true });\n\n// Handle cancellation\nrequestPromise.catch((error) =\u003e {\n  if (error.name === 'AbortError') {\n    console.log('Request canceled');\n  }\n});\n\n// Cancel\naborter.abort();\n```\n\nYou can specify any data as the `reason`.\nIf we want to pass an `object`, we can put the error message in the `message` field. The same object will be available as the `reason` in the case of the `AbortError` error.\n\n```javascript\n// Start the request\nconst requestPromise = aborter.try((signal) =\u003e fetch('/api/data', { signal }));\n\n// Handle cancellation\nrequestPromise.catch((error) =\u003e {\n  if (error instanceof AbortError) {\n    console.log(error.message); // Hello\n    console.log(error.reason); // { message: 'Hello', data: [] }\n  }\n});\n\n// Cancel\naborter.abort({ message: 'Hello', data: [] });\n```\n\nYou can also submit your own `AbortError` with your own settings.\n\n\u003e [!WARNING]\n\u003e Please be careful, the behavior of the `Aborter` function may be broken in its original form when reconfiguring the options.\n\n```javascript\n// Start the request\nconst requestPromise = aborter.try((signal) =\u003e fetch('/api/data', { signal }));\n\n// Handle cancellation\nrequestPromise.catch((error) =\u003e {\n  if (error instanceof AbortError) {\n    console.log(error.message); // 'Custom AbortError message'\n    console.log(error.reason); // 1\n  }\n});\n\n// Cancel\naborter.abort(new AbortError('Custom AbortError message', { reason: 1 }));\n```\n\n`abortWithRecovery(reason?)`\n\nImmediately cancels the currently executing request.\nAfter aborting, it restores the `AbortSignal`, resetting the `isAborted` property, and interaction with the `signal` property becomes available again.\n\n**Parameters:**\n\n- `reason?: any` - the reason for aborting the request (optional)\n\n**Returns:** `AbortController`\n\n**Examples:**\n\n```javascript\n// Create an Aborter instance\nconst aborter = new Aborter();\n\n// Data retrieval function\nconst fetchData = async () =\u003e {\n  try {\n    const data = await fetch('/api/data', { signal: aborter.signal });\n  } catch (error) {\n    // ALL errors, including cancellations, will go here\n    console.log(error);\n  }\n};\n\n// Calling a function with a request\nfetchData();\n\n// We interrupt the request and then restore the signal\naborter.abortWithRecovery();\n\n// Call the function again\nfetchData();\n```\n\n`dispose()`\n\n**Returns:** `void`\n\nClears the object's data completely: all subscriptions in all properties, clears overridden methods, state values.\n\n\u003e [!WARNING]\n\u003e The request does not interrupt!\n\n`static isError(error)`\n\nStatic method for checking if an object is an `AbortError` error.\n\n\u003e [!IMPORTANT]\n\u003e\n\u003e - The method will return `true` even if it receives a native AbortError that is thrown by the `DOMException` itself, or finds a hint of a request abort in the error message.\n\u003e - To exclusively verify that the error is an `AbortError` from the `saborter` package, it is better to use: `error instance AbortError`\n\n```javascript\ntry {\n  await aborter.try((signal) =\u003e fetch('/api/data', { signal }), { isErrorNativeBehavior: true });\n} catch (error) {\n  if (Aborter.isError(error)) {\n    console.log('This is a cancellation error');\n  } else {\n    console.log('Another error:', error);\n  }\n}\n\n// or\n\ntry {\n  await aborter.try((signal) =\u003e fetch('/api/data', { signal }), { isErrorNativeBehavior: true });\n} catch (error) {\n  if (error instanceof AbortError) {\n    console.log('This is a cancellation error');\n  } else {\n    console.log('Another error:', error);\n  }\n}\n```\n\n## 🔌 Additional APIs\n\n- [**AbortError**](./docs/abort-error.md) - Custom error for working with Aborter.\n- [**TimeoutError**](./docs/timeout-error.md) - Error for working with timeout interrupt.\n\n## ⚠️ Important Features\n\n### Error Handling\n\nBy default, the `try()` method does not reject the promise on `AbortError` (cancellation error). This prevents the `catch` block from being called when the request is canceled.\n\nIf you want the default behavior (the promise to be rejected on any error), use the `isErrorNativeBehavior` option:\n\n```javascript\n// The promise will be rejected even if an AbortError occurs\nconst result = await aborter\n  .try((signal) =\u003e fetch('/api/data', { signal }), { isErrorNativeBehavior: true })\n  .catch((error) =\u003e {\n    // ALL errors, including cancellations, will go here\n    if (error.name === 'AbortError') {\n      console.log('Cancelled');\n    }\n  });\n```\n\n### Resource Cleanup\n\nAlways abort requests when unmounting components or closing pages:\n\n```javascript\n// In React\nuseEffect(() =\u003e {\n  const aborter = new Aborter();\n\n  // Make requests\n\n  return () =\u003e {\n    aborter.abort(); // Clean up on unmount\n  };\n}, []);\n```\n\n### Finally block\n\nIgnoring `AbortError` cancellation errors, the `finally` block will only be executed if other errors are received, or if an abort error or the request succeeds.\n\n```javascript\nconst result = await aborter\n  .try((signal) =\u003e fetch('/api/data', { signal }))\n  .catch((error) =\u003e {\n    // Any error other than a request cancellation will be logged here.\n    console.log(error);\n  })\n  .finally(() =\u003e {\n    // The request was successfully completed or we caught a \"throw\"\n  });\n```\n\nEverything will also work if you use the `try-catch` syntax.\n\n```javascript\ntry {\n  const result = await aborter.try((signal) =\u003e fetch('/api/data', { signal }));\n} catch (error) {\n  // Any error other than a request cancellation will be logged here.\n  console.log(error);\n} finally {\n  // The request was successfully completed or we caught a \"throw\"\n}\n```\n\n\u003e [!WARNING]\n\u003e With the `isErrorNativeBehavior` flag enabled, the `finally` block will also be executed.\n\n## 🐜 Troubleshooting\n\n### Finally block\n\nMany people have probably encountered the problem with the `finally` block and the classic `AbortController`. When a request is canceled, the `catch` block is called. Why would `finally` block be called? This behavior only gets in the way and causes problems.\n\n**Example:**\n\n```javascript\nconst abortController = new AbortController();\n\nconst handleLoad = async () =\u003e {\n  try {\n    setLoading(true);\n\n    const users = await fetch('/api/users', { signal: abortController.signal });\n\n    setUsers(users);\n  } catch (error) {\n    if (error.name === 'AbortError') {\n      console.log('interrupt error handling');\n    }\n    console.log(error);\n  } finally {\n    if (abortController.signal.aborted) {\n      setLoading(false);\n    }\n  }\n};\n\nconst abortLoad = () =\u003e abortController.abort();\n```\n\nThe problem is obvious: checking the error by name, checking the condition to see if the `AbortController` was actually terminated in the `finally` block—it's all rather inconvenient.\n\nHow `Aborter` solves these problems:\n\n```javascript\nconst aborter = new Aborter();\n\nconst handleLoad = async () =\u003e {\n  try {\n    setLoading(true);\n\n    const users = await aborter.try(getUsers);\n\n    setUsers(users);\n  } catch (error) {\n    if (error instanceof AbortError) return;\n\n    console.log(error);\n  } finally {\n    setLoading(false);\n  }\n};\n\nconst abortLoad = () =\u003e aborter.abort();\n```\n\nThe name check is gone, replaced by an instance check. It's easy to make a typo in the error name and not be able to fix it. With `instanceof` this problem disappears.\nWith the `finally` block, everything has become even simpler. The condition that checked for termination is completely gone.\n\n\u003e [!NOTE]\n\u003e If you do not use the `abort()` method to terminate a request, then the check for `AbortError` in the `catch` block can be excluded.\n\n**Example:**\n\n```javascript\nconst aborter = new Aborter();\n\nconst handleLoad = async () =\u003e {\n  try {\n    setLoading(true);\n\n    const users = await aborter.try(getUsers);\n\n    setUsers(users);\n  } catch (error) {\n    console.log(error);\n  } finally {\n    setLoading(false);\n  }\n};\n```\n\n### Subsequent calls to the `try` method\n\nIf you want to cancel a group of requests combined in `Promise.all` or `Promise.allSettled` from a single `Aborter` instance, do not use multiple sequentially called `try` methods:\n\n```javascript\n// ✖️ Bad solution\nconst fetchData = async () =\u003e {\n  const [users, posts] = await Promise.all([\n    aborter.try((signal) =\u003e axios.get('/api/users', { signal })),\n    aborter.try((signal) =\u003e axios.get('/api/posts', { signal }))\n  ]);\n};\n```\n\n```javascript\n// ✔️ Good solution\nconst fetchData = async () =\u003e {\n  const [users, posts] = await aborter.try((signal) =\u003e {\n    return Promise.all([axios.get('/api/users', { signal }), axios.get('/api/posts', { signal })]);\n  });\n};\n```\n\nIn the case of the first solution, the second call to the `try` method will cancel the request of the first call, which will break your logic.\n\n## 🎯 Usage Examples\n\n### Example 1: Canceling multiple simultaneous requests\n\n```javascript\nconst aborter = new Aborter();\n\nconst getCategoriesByUserId = async (userId) =\u003e {\n  const data = await aborter.try(async (signal) =\u003e {\n    const user = await fetch(`/api/users/${userId}`, { signal });\n    const categories = await fetch(`/api/categories/${user.categoryId}`, { signal });\n\n    return [await user.json(), await categories.json()];\n  });\n\n  return data;\n};\n```\n\n### Example 2: Autocomplete\n\n```javascript\nclass SearchAutocomplete {\n  aborter = new Aborter();\n\n  search = async (query) =\u003e {\n    if (!query.trim()) return [];\n\n    try {\n      const results = await this.aborter.try(async (signal) =\u003e {\n        const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`, { signal });\n\n        return response.json();\n      });\n\n      this.displayResults(results);\n    } catch (error) {\n      // Get any error except AbortError\n      console.error('Search error:', error);\n    }\n  };\n\n  displayResults = (results) =\u003e {\n    // Display the results\n  };\n}\n```\n\n### Example 3: File Upload with Cancellation\n\n```javascript\nclass FileUploader {\n  constructor() {\n    this.aborter = new Aborter();\n    this.progress = 0;\n  }\n\n  uploadFile = async (file) =\u003e {\n    const formData = new FormData();\n    formData.append('file', file);\n\n    try {\n      await this.aborter.try(async (signal) =\u003e {\n        const response = await fetch('/api/upload', {\n          method: 'POST',\n          body: formData,\n          signal\n        });\n\n        // Track progress\n        const reader = response.body.getReader();\n        let receivedLength = 0;\n        const contentLength = +response.headers.get('Content-Length');\n\n        while (true) {\n          const { done, value } = await reader.read();\n          if (done) break;\n\n          receivedLength += value.length;\n          this.progress = Math.round((receivedLength / contentLength) * 100);\n        }\n      });\n\n      console.log('File uploaded successfully');\n    } catch (error) {\n      if (Aborter.isError(error)) {\n        console.log('Upload canceled');\n      } else {\n        console.error('Upload error:', error);\n      }\n    }\n  };\n\n  cancelUpload = () =\u003e {\n    this.aborter.abort();\n  };\n}\n```\n\n### Example 4: Integration with UI Frameworks\n\n**React**\n\n```javascript\nimport React, { useState, useEffect, useRef } from 'react';\nimport { Aborter } from 'saborter';\n\nconst DataFetcher = ({ url }) =\u003e {\n  const [data, setData] = useState(null);\n  const [loading, setLoading] = useState(false);\n  const aborterRef = useRef(new Aborter());\n\n  useEffect(() =\u003e {\n    return () =\u003e {\n      aborterRef.current.abort();\n    };\n  }, []);\n\n  const fetchData = async () =\u003e {\n    setLoading(true);\n    try {\n      const result = await aborterRef.current.try(async (signal) =\u003e {\n        const response = await fetch(url, { signal });\n        return response.json();\n      });\n      setData(result);\n    } catch (error) {\n      // Handle fetch error\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  const cancelRequest = () =\u003e {\n    aborterRef.current?.abort();\n  };\n\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={fetchData} disabled={loading}\u003e\n        {loading ? 'Loading...' : 'Load data'}\n      \u003c/button\u003e\n      \u003cbutton onClick={cancelRequest} disabled={!loading}\u003e\n        Cancel\n      \u003c/button\u003e\n      {data \u0026\u0026 \u003cpre\u003e{JSON.stringify(data, null, 2)}\u003c/pre\u003e}\n    \u003c/div\u003e\n  );\n};\n```\n\n**Vue.js**\n\n```javascript\nimport { Aborter } from 'saborter';\n\nexport default {\n  data() {\n    return {\n      aborter: null,\n      data: null,\n      loading: false\n    };\n  },\n  created() {\n    this.aborter = new Aborter();\n  },\n  beforeDestroy() {\n    this.aborter.abort();\n  },\n  methods: {\n    async fetchData() {\n      this.loading = true;\n      try {\n        this.data = await this.aborter.try(async (signal) =\u003e {\n          const response = await fetch(this.url, { signal });\n          return response.json();\n        });\n      } catch (error) {\n        // Handle fetch errors\n      } finally {\n        this.loading = false;\n      }\n    },\n    cancelRequest() {\n      this.aborter.abort();\n    }\n  }\n};\n```\n\n## 💻 Compatibility\n\n- **Browsers:** All modern browsers that support AbortController\n- **Node.js:** Requires a polyfill for AbortController (version 16+ has built-in support)\n- **TypeScript:** Full type support\n\n## 📋 License\n\nMIT License - see [LICENSE](./LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftensiile%2Fsaborter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftensiile%2Fsaborter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftensiile%2Fsaborter/lists"}