Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Added a new event `settled` to `EventListener` [#84](https://github.com/TENSIILE/saborter/pull/84/changes/34843174ea21c79f83142c328e326cd3dffff3ee)
- Added strict typing for the `isAbortError` function where the typeguard targets `AbortError` [#85](https://github.com/TENSIILE/saborter/pull/85/changes/efb8d5faa5029e580127b447c26ec860284f2fde)
- Added `Response` exception to the `catch` block when `response.ok` is `false` when using the short `fetch` format [#85](https://github.com/TENSIILE/saborter/pull/85/changes/d096d569aadd3ad6c7aa9e1b08a679e41fb0fe49)
- Added `debounce` option directly to `Aborter's` `try` method

### Bug Fixes

Expand Down
70 changes: 52 additions & 18 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ const results = await aborter.try(
Using the `Provision API`, an `aborter` can pass its data to either the `Fetch API` or the `XMLHttpRequest API` without passing it directly.
This allows it to work with libraries such as [`axios`](https://www.npmjs.com/package/axios), [`wretch`](https://www.npmjs.com/package/wretch), and [`ky`](https://www.npmjs.com/package/ky):

[Read more](#interrupting-requests-without-direct-signal-transmission-provision-api)

```javascript
// Create an Aborter instance
const aborter = new Aborter();
Expand All @@ -187,19 +189,20 @@ const results = await aborter.try(() => axios.get('/api/users').then((res) => re

### 4. Built-in debounce functionality

The `Aborter` class allows integration with the debounce utility:
You can use the `debounce` option directly inside the method:

```javascript
import { debounce } from 'saborter/lib';
[Read more](#working-with-debounce)

```javascript
// Create an Aborter instance
const aborter = new Aborter();

// The request will be delayed for 2 seconds and then executed.
const results = await aborter.try(
debounce((signal) => {
return fetch('/api/long-task', { signal });
}, 2000)
(signal) => {
return fetch('/api/data', { signal });
},
{ debounce: 2000 }
);
```

Expand All @@ -218,16 +221,6 @@ const results = await aborter.try((signal, { headers }) => {
});
```

If you want to use `Provision API`, you don't have to transfer anything:

```javascript
const aborter = new Aborter({ interruptionOnServer: true });

const results = await aborter.try(() => {
return fetch('/api/posts');
});
```

### 6. Interrupting promises without a signal

If you want to cancel a task with a promise:
Expand Down Expand Up @@ -636,7 +629,7 @@ requestPromise.catch((error) => {
}
});

// Cancel
// Abort
aborter.abort();
```

Expand Down Expand Up @@ -736,7 +729,6 @@ const aborter = new Aborter({
onAbort: (error) => {
if (error.type === 'cancelled') {
// handling request cancellation via a callback
}
}
});

Expand All @@ -751,6 +743,48 @@ const result = await aborter
});
```

### Working with debounce

The first way is the built-in `debounce` option in the `try` method:

```javascript
// Create an Aborter instance
const aborter = new Aborter();

// The request will be delayed for 2 seconds and then executed.
const results = await aborter.try(
(signal) => {
return fetch('/api/data', { signal });
},
{ debounce: 2000 }
);
```

> [!WARNING]
> The difference! This option defers the entire `try` method call, without running anything in the background.

> [!NOTE]
> We recommend using the option with `debounce` setting via the option.

You can use debounce in two ways, the second is through the `debounce` lib function:

```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.
const results = await aborter.try(
debounce((signal) => {
return fetch('/api/data', { signal });
}, 2000)
);
```

> [!WARNING]
> The difference! The function defers the callback itself, while the `try` method executes and logs all the processes that exist under the hood, such as recording an interrupt when the `timeout` expires and changing the request state to `"pending"`

### Interrupting requests without direct signal transmission (Provision API)

If you don't want to pass `Aborter` arguments to the `Fetch API` or `XMLHttpRequest API`, you don't have to. `Aborter` will automatically pass data from its context.
Expand Down
48 changes: 34 additions & 14 deletions src/modules/aborter/aborter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { RequestState, emitRequestState } from '../../features/state-observer';
import { AbortError, isAbortError } from '../../features/abort-error';
import { EventListener, clearEventListeners } from '../../features/event-listener';
import { injectAborterContextIntoHttpRequest, setAborterContextProvisionMode } from '../../features/lib/fetch';
import { debounce } from '../../features/lib/debounce';
import { ServerBreaker } from '../../features/server-breaker';
import { Timeout, TimeoutError } from '../../features/timeout';
import { ErrorMessage, disposeSymbol } from './aborter.constants';
Expand Down Expand Up @@ -113,21 +114,11 @@ export class Aborter implements Types.AborterType {
}
};

/**
* Performs an asynchronous request with cancellation of the previous request, preventing the call of the catch block when the request is canceled and the subsequent finally block.
* @param request callback function
* @param options an object that receives a set of settings for performing a request attempt
* @returns Promise
*/
public try<R = Response>(request: Types.AbortableRequest<Response>, options?: Types.FnTryOptions): Promise<R>;

public try<R>(request: Types.AbortableRequest<R>, options?: Types.FnTryOptions): Promise<R>;

public try<R>(
private tryImpl<R>(
request: Types.AbortableRequest<any>,
{ isErrorNativeBehavior = false, timeout, unpackData = true, provision = true }: Types.FnTryOptions = {}
{ isErrorNativeBehavior, timeout, unpackData, provision }: Types.FnTryOptions = {}
): Promise<R> {
setAborterContextProvisionMode(provision);
setAborterContextProvisionMode(!!provision);

if (this.isRequestInProgress) {
const cancelledAbortError = new AbortError(ErrorMessage.CancelRequest, {
Expand Down Expand Up @@ -162,7 +153,7 @@ export class Aborter implements Types.AborterType {

Promise.race([
request(this.abortController.signal, this.requestOptions),
Utils.createAbortablePromise(this.abortController.signal, { isErrorNativeBehavior })
Utils.createAbortablePromise(this.abortController.signal, { isErrorNativeBehavior: !!isErrorNativeBehavior })
])
.then((response) => {
if (!this.isRequestInProgress)
Expand Down Expand Up @@ -211,6 +202,35 @@ export class Aborter implements Types.AborterType {
return promise;
}

/**
* Performs an asynchronous request with cancellation of the previous request, preventing the call of the catch block when the request is canceled and the subsequent finally block.
* @param request callback function
* @param options an object that receives a set of settings for performing a request attempt
* @returns Promise
*/
public try<R = Response>(request: Types.AbortableRequest<Response>, options?: Types.FnTryOptions): Promise<R>;

public try<R>(request: Types.AbortableRequest<R>, options?: Types.FnTryOptions): Promise<R>;

public try<R>(
request: Types.AbortableRequest<any>,
{
isErrorNativeBehavior = false,
timeout,
unpackData = true,
provision = true,
debounce: debounceMs
}: Types.FnTryOptions = {}
): Promise<R> {
const certainOptions = { isErrorNativeBehavior, timeout, unpackData, provision };

if (debounceMs) {
return debounce<R>(() => this.tryImpl(request, certainOptions), debounceMs)(this.signal);
}

return this.tryImpl(request, certainOptions);
}

/**
* Calling this method sets the AbortSignal flag of this object and signals all observers that the associated action should be aborted.
*/
Expand Down
4 changes: 4 additions & 0 deletions src/modules/aborter/aborter.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export interface FnTryOptions {
* @default true
*/
provision?: boolean;
/**
* Delays the call to the `try` method in milliseconds.
*/
debounce?: number;
}

/**
Expand Down
Loading