lovell / lovell/sharp

Add support for `AbortSignal` to allow cancellation of in-flight image processing operations.

Open
#4,579 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
JavaScript
Stars
32.7k
Forks
1.4k
Avg merge
1d 14h
Merged PRs (30d)
5

Description

### What are you trying to achieve?

Add support for `AbortSignal` to allow cancellation of in-flight image processing operations.

Currently, once an image processing pipeline starts (e.g., `sharp(input).resize(...).toBuffer()`), there's no way to cancel it. This is problematic for:

- Long-running operations on large images or complex transformations
- HTTP servers that need to cancel processing when clients disconnect
- Interactive applications where users can cancel operations
- Resource-constrained environments where abandoned work should be stopped

The existing `timeout()` option provides time-based cancellation, but doesn't support event-driven or user-initiated cancellation patterns that are standard in modern Node.js APIs.

### When you searched for similar feature requests, what did you find that might be related?

Searched for: "abort", "cancel", "AbortSignal", "AbortController"

Found that the `timeout()` feature exists but is limited to time-based cancellation. There's no support for the standard `AbortSignal` API that's now ubiquitous in Node.js (fetch, streams, fs.promises, etc.).

The timeout mechanism uses libvips progress callbacks to stop processing, which provides a foundation that could be extended to support abort signals.

### What would you expect the API to look like?

```javascript
const controller = new AbortController();

// Option 1: Pass signal to constructor
const promise = sharp('input.jpg', { signal: controller.signal })
.resize(4000, 4000)
.toBuffer();

// Option 2: Pass signal to output methods
const promise = sharp('input.jpg')
.resize(4000, 4000)
.toBuffer({ signal: controller.signal });

// Option 3: Pass signal to toFile
const promise = sharp('input.jpg')
.resize(4000, 4000)
.toFile('output.jpg', { signal: controller.signal });

// Cancel the operation
controller.abort();

// Promise rejects with AbortError
// { name: 'AbortError', code: 'ABORT_ERR', message: 'The operation was aborted' }
```

The signal should be accepted in:
- `sharp()` constructor options
- `toBuffer()` options
- `toFile()` options

When aborted, processing should actually stop (not just discard the result) and reject with a standard `AbortError`.

### What alternatives have you considered?

1. **External timeout wrappers**: Using `Promise.race()` with timeouts. This doesn't actually stop the processing, just ignores the result, wasting CPU and memory.

2. **Worker thread management**: Running sharp in worker threads and terminating them. This adds complexity, doesn't integrate with Node.js cancellation patterns, and may leave resources in inconsistent states.

3. **Custom event system**: Building a sharp-specific cancellation API. This would be non-standard and require developers to learn a new pattern.

The `AbortSignal` API is the standard across Node.js and web APIs, so supporting it aligns sharp with modern JavaScript patterns and provides a familiar interface for developers.

### Please provide sample image(s) that help explain this feature

Example use case - HTTP server with request cancellation:

```javascript
app.get('/image/:id', (req, res) => {
const controller = new AbortController();

// Cancel processing if client disconnects
req.on('close', () => controller.abort());

sharp(imagePath)
.resize(800)
.toBuffer({ signal: controller.signal })
.then(buffer => res.send(buffer))
.catch(err => {
if (err.name === 'AbortError') {
console.log('Processing cancelled');
return;
}
res.status(500).send(err.message);
});
});
```

This pattern is essential for building efficient HTTP services that don't waste resources on abandoned requests.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the existing timeout feature and its libvips progress-callback mechanism, then inspect the sharp() constructor, toBuffer(), and toFile() option entry points named in the issue. Verify the chosen API accepts AbortSignal, stops in-flight processing, and rejects with the specified AbortError behavior, including cancellation through the HTTP-server example.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
api, backend, performance
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.