AbortSignal / Timeouts

Overview

Abort controllers allow you to cancel ongoing LLM operations, which is essential for:

  • User-initiated cancellations (e.g., “Stop generating” buttons)
  • Implementing timeouts for long-running operations
  • Cleaning up resources when components unmount
  • Managing multiple parallel requests

Quick Start

1import { b } from '@/baml_client'
2
3// TypeScript uses AbortSignal for cancellation
4// No additional imports needed - it's built into the runtime
5
6// Modern approach: Use AbortSignal.timeout() for automatic timeout
7try {
8 const result = await b.ExtractResume(text, {
9 signal: AbortSignal.timeout(5000) // 5 second timeout
10 })
11} catch (error) {
12 if (error.name === 'BamlAbortError') {
13 console.log('Operation was cancelled')
14 }
15}
16
17// Manual approach: Create controller and cancel later
18const controller = new AbortController()
19const promise = b.ExtractResume(text, {
20 signal: controller.signal
21})
22
23// Cancel after 5 seconds
24setTimeout(() => controller.abort(), 5000)
25
26try {
27 const result = await promise
28} catch (error) {
29 if (error.name === 'BamlAbortError') {
30 console.log('Operation was cancelled')
31 }
32}

Basic Examples

Implementing Timeouts

Automatically cancel operations that take too long:

1// Modern approach using AbortSignal.timeout()
2async function extractWithTimeout(text: string, timeoutMs: number = 30000) {
3 try {
4 const result = await b.ExtractResume(text, {
5 signal: AbortSignal.timeout(timeoutMs)
6 })
7 return result
8 } catch (error) {
9 if (error.name === 'BamlAbortError') {
10 throw new Error(`Operation timed out after ${timeoutMs}ms`)
11 }
12 throw error
13 }
14}
15
16// Manual implementation (for when you need more control)
17async function extractWithManualTimeout(text: string, timeoutMs: number = 30000) {
18 const controller = new AbortController()
19
20 // Set up automatic timeout
21 const timeoutId = setTimeout(() => {
22 controller.abort('timeout')
23 }, timeoutMs)
24
25 try {
26 const result = await b.ExtractResume(text, {
27 signal: controller.signal
28 })
29 clearTimeout(timeoutId)
30 return result
31 } catch (error) {
32 clearTimeout(timeoutId)
33 if (error.name === 'BamlAbortError') {
34 throw new Error(`Operation timed out after ${timeoutMs}ms`)
35 }
36 throw error
37 }
38}

User-Initiated Cancellation

Build responsive backend services that allow users to cancel long-running operations:

1import express from 'express'
2import { b } from '@/baml_client'
3
4const app = express()
5const activeControllers = new Map<string, AbortController>()
6
7app.post('/extract/:requestId', async (req, res) => {
8 const { requestId } = req.params
9 const { text } = req.body
10
11 const controller = new AbortController()
12 activeControllers.set(requestId, controller)
13
14 try {
15 const result = await b.ExtractResume(text, {
16 signal: controller.signal
17 })
18 res.json({ result })
19 } catch (error) {
20 if (error.name === 'BamlAbortError') {
21 res.json({ status: 'cancelled' })
22 } else {
23 res.status(500).json({ error: error.message })
24 }
25 } finally {
26 activeControllers.delete(requestId)
27 }
28})
29
30app.post('/cancel/:requestId', (req, res) => {
31 const { requestId } = req.params
32 const controller = activeControllers.get(requestId)
33
34 if (controller) {
35 controller.abort()
36 res.json({ status: 'cancellation requested' })
37 } else {
38 res.status(404).json({ status: 'request not found' })
39 }
40})

Streaming with Abort Controllers

Abort controllers work seamlessly with streaming responses:

1const controller = new AbortController()
2
3const stream = b.stream.GenerateStory(prompt, {
4 signal: controller.signal
5})
6
7let wordCount = 0
8try {
9 for await (const chunk of stream) {
10 wordCount += chunk.split(' ').length
11
12 // Stop if we've generated enough
13 if (wordCount > 1000) {
14 controller.abort('word limit reached')
15 break
16 }
17
18 // Process chunk
19 console.log(chunk)
20 }
21} catch (error) {
22 if (error instanceof BamlAbortError) {
23 console.log('Stream cancelled:', error.reason)
24 }
25}

Error Handling

Properly handle abort errors to distinguish cancellations from other failures:

1import { BamlAbortError } from '@/baml_client'
2
3try {
4 const result = await b.ExtractResume(text, {
5 signal: controller.signal
6 })
7 return { success: true, data: result }
8} catch (error) {
9 if (error instanceof BamlAbortError) {
10 // User cancelled - this is expected
11 return { success: false, cancelled: true }
12 }
13
14 if (error.name === 'BamlValidationError') {
15 // Schema validation failed
16 return { success: false, validationError: error.message }
17 }
18
19 // Unexpected error
20 console.error('Extraction failed:', error)
21 throw error
22}

Best Practices

When to Use Each Pattern

1// ✅ Use AbortSignal.timeout() for simple timeouts
2const result = await b.ExtractResume(text, {
3 signal: AbortSignal.timeout(30000)
4})
5
6// ✅ Use manual AbortController when you need to cancel conditionally
7const controller = new AbortController()
8const promise = b.ExtractResume(text, {
9 signal: controller.signal
10})
11
12// Cancel based on user action or business logic
13if (shouldCancel) {
14 controller.abort('cancelled by user')
15}
16
17// ✅ Combine both patterns for timeout + manual control
18const controller = new AbortController()
19const timeoutId = setTimeout(() => controller.abort('timeout'), 30000)
20
21const result = await b.ExtractResume(text, {
22 signal: controller.signal
23})
24
25clearTimeout(timeoutId)

Key Benefits

  • AbortSignal.timeout(): Cleaner code for simple timeout scenarios
  • Manual AbortController: More control over cancellation logic and reasons
  • Better Error Handling: Clear distinction between timeouts and user cancellations
  • Standards Compliance: Uses modern web standards that work across different environments

Advanced Patterns

For more advanced abort controller patterns including:

  • Cancelling parallel operations - Cancel multiple concurrent calls at once or individually
  • Fastest request wins - Race multiple LLM providers and cancel slower ones
  • Implementing timeouts for parallel operations - Set automatic timeouts for batches of operations
  • Batching with cancellation support - Process items in batches with cancellation

See the Concurrent Calls guide for detailed examples and implementations.