# Cancel requests and report progress

Use [`ky`](https://bench-ky-56.atloria.app/p/bench-ky-56-cVfxjVA4Qg/developer/ky#default) with an `AbortSignal` to cancel a request, and use the progress callbacks in [`Options`](https://bench-ky-56.atloria.app/p/bench-ky-56-cVfxjVA4Qg/developer/ky-options#options) to update upload or download status when the runtime supports streaming. The callback receives a [`Progress`](https://bench-ky-56.atloria.app/p/bench-ky-56-cVfxjVA4Qg/developer/ky#progress) object and the current byte chunk.

## When to use this

Use cancellation when a view closes, a newer request supersedes an older one, or the user explicitly stops a transfer. Use progress callbacks when a streamed body is large enough that the user needs feedback during the transfer.

## Cancel a request

Create an `AbortController`, pass its `signal`, and call `abort()` from the event that stops the work. The request rejects with an abort error, so handle that case separately from other failures.

```ts
import ky from 'ky';

const run = async (): Promise<void> => {
	const controller = new AbortController();
	const {signal} = controller;
	const requestUrl = 'https://example.com/long-running-report';

 	const request = ky.get(requestUrl, {signal}).text();

 	setTimeout(() => {
		controller.abort();
	}, 5000);

	try {
		const report = await request;
		console.log(report);
	} catch (error) {
		if (error instanceof Error && error.name === 'AbortError') {
			console.log('Request cancelled');
		} else {
			throw error;
		}
	}
};

void run();
```

The request stops when the controller is aborted; the `catch` block identifies that cancellation by the error name and rethrows unrelated errors.

## Report download progress

Pass `onDownloadProgress` to the request. Ky calls it as response chunks arrive. `percent` ranges from `0` to `1`, `transferredBytes` counts received bytes, and `totalBytes` is an estimate that can be `0` when the size is unavailable.

```ts
import ky from 'ky';

const run = async (): Promise<void> => {
	const downloadUrl = 'https://example.com/files/archive.zip';

 	const response = await ky.get(downloadUrl, {
		onDownloadProgress: (progress, chunk) => {
			const percentage = Math.round(progress.percent * 100);
			console.log(`${percentage}%`, progress.transferredBytes, progress.totalBytes, chunk.byteLength);
		},
	});

 	const archive = await response.blob();
 	console.log(archive.size);
};

void run();
```

The callback reports each received chunk, including an empty final chunk when the response body is empty. The final progress report has `percent` equal to `1`; `totalBytes` can still reflect an estimate rather than a server-provided exact size.

## Report upload progress

Pass the upload body and `onUploadProgress` to a method shortcut such as `ky.post()`. A `Blob` gives Ky a body size it can use for the progress estimate.

```ts
import ky from 'ky';

const run = async (): Promise<void> => {
	const upload = new Blob(['row,quantity\nwidget,12\n'], {type: 'text/csv'});

 	await ky.post('https://example.com/uploads/inventory.csv', {
		body: upload,
		onUploadProgress: (progress, chunk) => {
			const percentage = Math.round(progress.percent * 100);
			console.log(`${percentage}%`, progress.transferredBytes, progress.totalBytes, chunk.byteLength);
		},
	});
};

void run();
```

The callback reports bytes as Ky sends the request body and finishes with a progress value of `1`. The upload callback is silently ignored when request stream support is unavailable, when `keepalive` is `true`, or when the effective request mode is `'no-cors'`; do not use it as the only indication that an upload started or finished in those environments.

## Options

| Option | Type | Default | What it does |
| --- | --- | --- | --- |
| `signal` | `AbortSignal \| null \| undefined` | — | Supplies the signal that aborts the request. |
| `onDownloadProgress` | `(progress, chunk) => void` | — | Receives download progress and each received `Uint8Array` chunk. |
| `onUploadProgress` | `(progress, chunk) => void` | — | Receives upload progress and each sent `Uint8Array` chunk. |
| `keepalive` | `boolean` | — | When `true`, prevents Ky from using a streamed request body for upload progress. |

## Pitfalls

- Cancellation is cooperative: keep the controller and call `abort()` from the owner of the request, such as a cancel button or a component cleanup handler.
- `totalBytes` can be `0` when the transfer size cannot be determined. Display transferred bytes or an indeterminate indicator instead of treating `0` as a zero-byte transfer.
- Upload progress depends on request-stream support and HTTP/2 for HTTPS connections in Chromium-based browsers. Check the runtime requirements before making a determinate upload progress bar part of the interface.

For reusable URL or retry defaults, see [shared clients and URL defaults](https://bench-ky-56.atloria.app/p/bench-ky-56-cVfxjVA4Qg/developer/shared-clients-and-url-defaults). For Ky's failure categories, see [Ky's error model](https://bench-ky-56.atloria.app/p/bench-ky-56-cVfxjVA4Qg/developer/ky-error-model).
