diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f54936..1c62238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Saborter Changelog +## v2.1.0 (March 18th, 2026) + +### New Features + +- Added break promises without signal [#56](https://github.com/TENSIILE/saborter/pull/56) + ## v2.0.1 (March 1th, 2026) ### Bug Fixes diff --git a/package.json b/package.json index 2c8d336..a205593 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "saborter", - "version": "2.0.1", + "version": "2.1.0", "description": "A simple and efficient library for canceling asynchronous requests using AbortController", "main": "dist/index.cjs.js", "module": "dist/index.es.js", diff --git a/readme.md b/readme.md index 5ce2293..2d690c0 100644 --- a/readme.md +++ b/readme.md @@ -71,6 +71,7 @@ We constantly encounter situations where an incipient request needs to be cancel | The signal will always be new. It's no coincidence that a previously disabled signal can appear from outside, which breaks all logic. | ✅ | ❌ | | Built-in debounce functionality. | ✅ | ❌ | | Availability of ponyfills in the set. | ✅ | ❌ | +| Interrupting promises. | ✅ | ❌ | ## 🚀 Quick Start @@ -113,11 +114,12 @@ handleSearch('ab'); // The first request is canceled, a new one is started handleSearch('abc'); // The second request is canceled, a new one is started ``` -### 2. Automatic cancellation of requests +### 2. Automatic cancellation of requests by timer The `Aborter` class makes it easy to cancel running requests after a period of time: ```javascript +// Create an Aborter instance const aborter = new Aborter(); // Start a long-running request and cancel the request after 2 seconds @@ -136,6 +138,7 @@ The `Aborter` class allows integration with the debounce utility: ```javascript import { debounce } from 'saborter/lib'; +// Create an Aborter instance const aborter = new Aborter(); // The request will be delayed for 2 seconds and then executed. @@ -146,11 +149,35 @@ const results = aborter.try( ); ``` -### 4. Multiple request aborts through a single `ReusableAborter` instance +### 4. Interrupting promises without a signal + +If you want to cancel a task with a promise: + +```javascript +import { Aborter } from 'saborter'; + +// Create an Aborter instance +const aborter = new Aborter(); + +// Create a helper function for delay +const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + +// The callback function can be restarted each time (by calling .try() again), which will interrupt the previous call and start it again. +// Or the `.abort()` method can be used to abort the callback function entirely. +const results = aborter.try( + async () => { + await delay(2000); + return Promise.resolve({ done: true }); + } +); +``` + +### 5. Multiple request aborts through a single `ReusableAborter` instance The `ReusableAborter` class allows you to easily cancel requests an unlimited number of times while preserving all listeners: ```javascript +// Create an ReusableAborter instance const reusableAborter = new ReusableAborter(); // Adding a subscription to an interrupt event @@ -159,7 +186,6 @@ reusableAborter.addEventListener('abort', (e) => console.log('aborted', e)); // Start a long-running request and cancel the request after 2 seconds const fetchPosts = async () => { const response = await fetch('/api/posts', { signal: reusableAborter.signal }); - return await response.json(); }; @@ -167,7 +193,7 @@ reusableAborter.abort(); // call of the listener -> console.log('aborted', e) reusableAborter.abort(); // listener recall -> console.log('aborted', e) ``` -### 5. Working with Multiple Requests +### 6. Working with Multiple Requests You can create separate instances for different groups of requests: @@ -301,6 +327,8 @@ const result = await aborter.try(async (signal) => { }); ``` +**Automatic response unpacking:** + You can either write `.json()` or not write it from the `fetch` function. You can immediately return the `fetch` result. > [!NOTE] @@ -315,11 +343,11 @@ const users = await aborter.try((signal) => { > [!WARNING] > You cannot override typing via a generic if the callback already has a specific return type. -```javascript +```typescript const users = await aborter.try(async (signal) => { const response = await fetch('/api/users', { signal }); // There will be a typing error! - return await response.json() as { data: User[] } + return (await response.json()) as { data: User[] }; }); ``` @@ -338,6 +366,42 @@ const response = await aborter.try( const data = await response.json(); ``` +**Calling a method without a signal:** + +The `.try()` method can be called without removing the `signal` from the callback argument if you don't need it. +For example, if there's a promise you want to break, you can call it inside the callback. + +```typescript +const results = aborter.try(async () => { + await delay(2000); + return Promise.resolve({ done: true }); +}); +``` + +It is also possible to abort requests (the same `fetch()`, for example) without transmitting a `signal`, but this is highly discouraged. +However, if it is not possible to transmit a `signal`, the aborter will abort its promise. + +```typescript +// We create a wrapper around the request without being able to pass the signal +const requestPosts = async () => { + const response = await fetch('/api/posts'); + return response.json(); +}; + +try { + // We launch an HTTP request, after 1.5 seconds it will automatically terminate + const posts = await aborter.try(requestPosts, { timeout: 1500 }); + setPosts(posts); +} catch (error) { + if (error instanceof AbortError) { + // We get an interrupt error and the setPosts setter won't be called + } +} +``` + +> [!NOTE] +> In this case, the wait for the request to be executed will be interrupted, but the request itself will still be executed. + **Examples using automatic cancellation after a time:** ```javascript diff --git a/unreleased-packages/saborter-2.1.0.tgz b/unreleased-packages/saborter-2.1.0.tgz new file mode 100644 index 0000000..c078d45 Binary files /dev/null and b/unreleased-packages/saborter-2.1.0.tgz differ