Framework
Version
Debouncer API Referenz
Throttler API Referenz
Rate Limiter API Referenz
Queue API Referenz
Batcher API Referenz

Async Batching-Anleitung

Alle Kernkonzepte aus dem Batching Guide gelten auch für asynchrones Batching.

Wann asynchrones Batching verwenden

Während der synchrone Batcher für viele Anwendungsfälle gut funktioniert, bietet asynchrones Batching zusätzliche Funktionen, die besonders nützlich sind, wenn

  • Sie den Rückgabewert von Batch-Ausführungen erfassen und verwenden müssen
  • Ihre Batch-Verarbeitung asynchrone Operationen beinhaltet (API-Aufrufe, Datenbankoperationen, Datei-E/A)
  • Sie eine erweiterte Fehlerbehandlung mit konfigurierbarem Fehlerverhalten benötigen
  • Sie Erfolgs-/Fehlerstatistiken separat verfolgen möchten
  • Sie überwachen müssen, wann Batches aktiv ausgeführt werden

Asynchrones Batching in TanStack Pacer

TanStack Pacer bietet asynchrones Batching über die Klasse AsyncBatcher und die Funktion asyncBatch. Im Gegensatz zur synchronen Version verarbeitet der asynchrone Batcher Promises und bietet robuste Fehlerbehandlungsfunktionen.

Grundlegende Verwendung mit asyncBatch

Die Funktion asyncBatch bietet eine einfache Möglichkeit, eine asynchrone Batching-Funktion zu erstellen.

ts
import { asyncBatch } from '@tanstack/pacer'

const processAsyncBatch = asyncBatch<number>(
  async (items) => {
    // Process the batch asynchronously
    const results = await Promise.all(
      items.map(item => processApiCall(item))
    )
    return results
  },
  {
    maxSize: 3,
    wait: 2000,
    onSuccess: (results, batch, batcher) => {
      console.log('Batch completed successfully:', results)
      console.log('Processed batch:', batch)
      console.log('Total successes:', batcher.store.state.successCount)
    },
    onError: (error, batch, batcher) => {
      console.error('Batch failed:', error)
      console.log('Failed batch:', batch)
      console.log('Total errors:', batcher.store.state.errorCount)
    }
  }
)

// Add items to be batched
processAsyncBatch(1)
processAsyncBatch(2)
processAsyncBatch(3) // Triggers batch processing
import { asyncBatch } from '@tanstack/pacer'

const processAsyncBatch = asyncBatch<number>(
  async (items) => {
    // Process the batch asynchronously
    const results = await Promise.all(
      items.map(item => processApiCall(item))
    )
    return results
  },
  {
    maxSize: 3,
    wait: 2000,
    onSuccess: (results, batch, batcher) => {
      console.log('Batch completed successfully:', results)
      console.log('Processed batch:', batch)
      console.log('Total successes:', batcher.store.state.successCount)
    },
    onError: (error, batch, batcher) => {
      console.error('Batch failed:', error)
      console.log('Failed batch:', batch)
      console.log('Total errors:', batcher.store.state.errorCount)
    }
  }
)

// Add items to be batched
processAsyncBatch(1)
processAsyncBatch(2)
processAsyncBatch(3) // Triggers batch processing

Hinweis: Bei der Verwendung von React bevorzugen Sie den Hook useAsyncBatchedCallback gegenüber der Funktion asyncBatch für eine bessere Integration mit dem Lebenszyklus von React und die automatische Bereinigung.

Erweiterte Verwendung mit der AsyncBatcher Klasse

Für mehr Kontrolle über das asynchrone Batch-Verhalten verwenden Sie die Klasse AsyncBatcher direkt.

ts
import { AsyncBatcher } from '@tanstack/pacer'

const batcher = new AsyncBatcher<number>(
  async (items) => {
    // Process the batch asynchronously
    const results = await Promise.all(
      items.map(item => processApiCall(item))
    )
    return results
  },
  {
    maxSize: 5,
    wait: 3000,
    onSuccess: (results, batch, batcher) => {
      console.log('Batch succeeded:', results)
      console.log('Processed batch:', batch)
    },
    onError: (error, batch, batcher) => {
      console.error('Batch failed:', error)
      console.log('Failed batch:', batch)
    }
  }
)

// Access current state via TanStack Store
console.log(batcher.store.state.successCount) // Number of successful batch executions
console.log(batcher.store.state.errorCount) // Number of failed batch executions
console.log(batcher.store.state.isExecuting) // Whether a batch is currently executing
console.log(batcher.store.state.lastResult) // Result from most recent batch

// Add items to the batch
batcher.addItem(1)
batcher.addItem(2)

// Control batch execution
batcher.stop()  // Stop processing
batcher.start() // Resume processing
import { AsyncBatcher } from '@tanstack/pacer'

const batcher = new AsyncBatcher<number>(
  async (items) => {
    // Process the batch asynchronously
    const results = await Promise.all(
      items.map(item => processApiCall(item))
    )
    return results
  },
  {
    maxSize: 5,
    wait: 3000,
    onSuccess: (results, batch, batcher) => {
      console.log('Batch succeeded:', results)
      console.log('Processed batch:', batch)
    },
    onError: (error, batch, batcher) => {
      console.error('Batch failed:', error)
      console.log('Failed batch:', batch)
    }
  }
)

// Access current state via TanStack Store
console.log(batcher.store.state.successCount) // Number of successful batch executions
console.log(batcher.store.state.errorCount) // Number of failed batch executions
console.log(batcher.store.state.isExecuting) // Whether a batch is currently executing
console.log(batcher.store.state.lastResult) // Result from most recent batch

// Add items to the batch
batcher.addItem(1)
batcher.addItem(2)

// Control batch execution
batcher.stop()  // Stop processing
batcher.start() // Resume processing

Hauptunterschiede zum synchronen Batching

1. Rückgabewert-Handling

Im Gegensatz zum synchronen Batcher, der void zurückgibt, ermöglicht die asynchrone Version, den Rückgabewert Ihrer Batch-Funktion zu erfassen und zu verwenden.

ts
const batcher = new AsyncBatcher<string>(
  async (items) => {
    const results = await processBatch(items)
    return results
  },
  {
    maxSize: 5,
    onSuccess: (results, batch, batcher) => {
      // Handle the returned results
      console.log('Batch results:', results)
      console.log('Processed batch:', batch)
    }
  }
)
const batcher = new AsyncBatcher<string>(
  async (items) => {
    const results = await processBatch(items)
    return results
  },
  {
    maxSize: 5,
    onSuccess: (results, batch, batcher) => {
      // Handle the returned results
      console.log('Batch results:', results)
      console.log('Processed batch:', batch)
    }
  }
)

2. Fehlerbehandlung

Der asynchrone Batcher bietet umfassende Fehlerbehandlungsfunktionen.

ts
const batcher = new AsyncBatcher<number>(
  async (items) => {
    // This might throw an error
    const results = await riskyBatchOperation(items)
    return results
  },
  {
    maxSize: 3,
    onError: (error, batch, batcher) => {
      // Handle batch errors
      console.error('Batch processing failed:', error)
      console.log('Items that failed:', batch)
      console.log('Total error count:', batcher.store.state.errorCount)
    },
    throwOnError: false, // Don't throw errors, just handle them
    onSuccess: (results, batch, batcher) => {
      console.log('Batch succeeded:', results)
      console.log('Processed batch:', batch)
      console.log('Total success count:', batcher.store.state.successCount)
    },
    onSettled: (batch, batcher) => {
      // Called after every batch (success or failure)
      console.log('Batch settled:', batch)
      console.log('Total batches:', batcher.store.state.settleCount)
    }
  }
)
const batcher = new AsyncBatcher<number>(
  async (items) => {
    // This might throw an error
    const results = await riskyBatchOperation(items)
    return results
  },
  {
    maxSize: 3,
    onError: (error, batch, batcher) => {
      // Handle batch errors
      console.error('Batch processing failed:', error)
      console.log('Items that failed:', batch)
      console.log('Total error count:', batcher.store.state.errorCount)
    },
    throwOnError: false, // Don't throw errors, just handle them
    onSuccess: (results, batch, batcher) => {
      console.log('Batch succeeded:', results)
      console.log('Processed batch:', batch)
      console.log('Total success count:', batcher.store.state.successCount)
    },
    onSettled: (batch, batcher) => {
      // Called after every batch (success or failure)
      console.log('Batch settled:', batch)
      console.log('Total batches:', batcher.store.state.settleCount)
    }
  }
)

3. Nachverfolgung des Ausführungsstatus

Der asynchrone Batcher verfolgt, wann Batches aktiv ausgeführt werden.

ts
const batcher = new AsyncBatcher<number>(
  async (items) => {
    console.log('Starting batch execution...')
    const results = await longRunningBatchOperation(items)
    console.log('Batch execution completed')
    return results
  },
  {
    maxSize: 5,
    onItemsChange: (batcher) => {
      console.log('Is executing:', batcher.store.state.isExecuting)
      console.log('Items in queue:', batcher.store.state.size)
    }
  }
)
const batcher = new AsyncBatcher<number>(
  async (items) => {
    console.log('Starting batch execution...')
    const results = await longRunningBatchOperation(items)
    console.log('Batch execution completed')
    return results
  },
  {
    maxSize: 5,
    onItemsChange: (batcher) => {
      console.log('Is executing:', batcher.store.state.isExecuting)
      console.log('Items in queue:', batcher.store.state.size)
    }
  }
)

4. Unterschiedliche Rückrufe

Der AsyncBatcher unterstützt diese asynchronen Rückrufe:

  • onSuccess: Wird nach jeder erfolgreichen Batch-Ausführung aufgerufen und liefert das Ergebnis, den verarbeiteten Batch und die Batcher-Instanz.
  • onError: Wird aufgerufen, wenn eine Batch-Ausführung fehlschlägt, und liefert den Fehler, die fehlgeschlagenen Elemente des Batches und die Batcher-Instanz.
  • onSettled: Wird nach jeder Batch-Ausführung (erfolgreich oder mit Fehlern) aufgerufen und liefert den verarbeiteten Batch und die Batcher-Instanz.
  • onExecute: Wird nach jeder Batch-Ausführung aufgerufen und liefert den verarbeiteten Batch und die Batcher-Instanz (wie beim synchronen Batcher).
  • onItemsChange: Wird aufgerufen, wenn Elemente hinzugefügt oder der Batch verarbeitet wird.

Optionen zur Fehlerbehandlung

Der asynchrone Batcher bietet flexible Fehlerbehandlung durch die Option throwOnError.

ts
const batcher = new AsyncBatcher<number>(
  async (items) => {
    // This might throw an error
    throw new Error('Batch processing failed')
  },
  {
    maxSize: 3,
    onError: (error, batch, batcher) => {
      console.error('Handling error:', error)
    },
    throwOnError: true, // Will throw errors even with onError handler
    // throwOnError: false, // Will swallow errors (default if onError is provided)
    // throwOnError: undefined, // Uses default behavior based on onError presence
  }
)
const batcher = new AsyncBatcher<number>(
  async (items) => {
    // This might throw an error
    throw new Error('Batch processing failed')
  },
  {
    maxSize: 3,
    onError: (error, batch, batcher) => {
      console.error('Handling error:', error)
    },
    throwOnError: true, // Will throw errors even with onError handler
    // throwOnError: false, // Will swallow errors (default if onError is provided)
    // throwOnError: undefined, // Uses default behavior based on onError presence
  }
)
  • Standardverhalten: throwOnError ist true, wenn kein onError Handler bereitgestellt wird, und false, wenn ein onError Handler bereitgestellt wird.
  • Mit onError Handler: Der Handler wird zuerst aufgerufen, dann wird der Fehler ausgelöst, wenn throwOnError true ist.
  • Fehlerzustand: Fehlgeschlagene Elemente werden im Array failedItems verfolgt und können über peekFailedItems() abgerufen werden. Der onError Rückruf erhält den Batch der fehlgeschlagenen Elemente, nicht die gesammelten fehlgeschlagenen Elemente.

Dynamische Optionen

Wie der synchrone Batcher unterstützt der asynchrone Batcher dynamische Optionen.

ts
const batcher = new AsyncBatcher<number>(
  async (items) => {
    return await processBatch(items)
  },
  {
    // Dynamic batch size based on success rate
    maxSize: (batcher) => {
      const successRate = batcher.store.state.successCount / Math.max(1, batcher.store.state.settleCount)
      return successRate > 0.8 ? 10 : 5 // Larger batches if success rate is high
    },
    // Dynamic wait time based on error count
    wait: (batcher) => {
      return batcher.store.state.errorCount > 5 ? 5000 : 2000 // Wait longer if errors are frequent
    }
  }
)
const batcher = new AsyncBatcher<number>(
  async (items) => {
    return await processBatch(items)
  },
  {
    // Dynamic batch size based on success rate
    maxSize: (batcher) => {
      const successRate = batcher.store.state.successCount / Math.max(1, batcher.store.state.settleCount)
      return successRate > 0.8 ? 10 : 5 // Larger batches if success rate is high
    },
    // Dynamic wait time based on error count
    wait: (batcher) => {
      return batcher.store.state.errorCount > 5 ? 5000 : 2000 // Wait longer if errors are frequent
    }
  }
)

Ausstehende Batches leeren

Der asynchrone Batcher unterstützt das Leeren ausstehender Batches, um die Verarbeitung sofort auszulösen.

ts
const batcher = new AsyncBatcher(asyncBatchFn, { maxSize: 10, wait: 5000 })

batcher.addItem('item1')
batcher.addItem('item2')
console.log(batcher.store.state.isPending) // true

// Flush immediately instead of waiting
const result = await batcher.flush()
console.log('Flush result:', result)
console.log(batcher.store.state.isEmpty) // true (batch was processed)
const batcher = new AsyncBatcher(asyncBatchFn, { maxSize: 10, wait: 5000 })

batcher.addItem('item1')
batcher.addItem('item2')
console.log(batcher.store.state.isPending) // true

// Flush immediately instead of waiting
const result = await batcher.flush()
console.log('Flush result:', result)
console.log(batcher.store.state.isEmpty) // true (batch was processed)

Zustandsverwaltung

Die Klasse AsyncBatcher verwendet TanStack Store für reaktives Zustandsmanagement und bietet Echtzeit-Zugriff auf den Batch-Ausführungsstatus, die Fehlerverfolgung und die Verarbeitungsstatistiken. Alle Zustandsinformationen werden in einem TanStack Store gespeichert und können über asyncBatcher.store.state abgerufen werden. Wenn Sie jedoch einen Framework-Adapter wie React oder Solid verwenden, sollten Sie den Zustand nicht von hier lesen. Stattdessen lesen Sie den Zustand von asyncBatcher.state und stellen eine Selektor-Callback als drittes Argument für den Hook useAsyncBatcher bereit, um sich für die Zustandsverfolgung zu opt-in, wie unten gezeigt.

Zustandsselektor (Framework-Adapter)

Framework-Adapter unterstützen ein selector-Argument, das es Ihnen ermöglicht, anzugeben, welche Zustandsänderungen Neu-Renderings auslösen. Dies optimiert die Leistung, indem unnötige Neu-Renderings bei irrelevanten Zustandsänderungen verhindert werden.

Standardmäßig ist asyncBatcher.state leer ({}), da der Selektor standardmäßig leer ist. Hier werden reaktive Zustände aus einem TanStack Store useStore gespeichert. Sie müssen sich für die Zustandsverfolgung entscheiden, indem Sie eine Selektor-Funktion bereitstellen.

ts
// Default behavior - no reactive state subscriptions
const batcher = useAsyncBatcher(asyncBatchFn, { maxSize: 5, wait: 1000 })
console.log(batcher.state) // {}

// Opt-in to re-render when isExecuting changes
const batcher = useAsyncBatcher(
  asyncBatchFn, 
  { maxSize: 5, wait: 1000 },
  (state) => ({ isExecuting: state.isExecuting })
)
console.log(batcher.state.isExecuting) // Reactive value

// Multiple state properties
const batcher = useAsyncBatcher(
  asyncBatchFn,
  { maxSize: 5, wait: 1000 },
  (state) => ({
    isExecuting: state.isExecuting,
    successCount: state.successCount,
    errorCount: state.errorCount
  })
)
// Default behavior - no reactive state subscriptions
const batcher = useAsyncBatcher(asyncBatchFn, { maxSize: 5, wait: 1000 })
console.log(batcher.state) // {}

// Opt-in to re-render when isExecuting changes
const batcher = useAsyncBatcher(
  asyncBatchFn, 
  { maxSize: 5, wait: 1000 },
  (state) => ({ isExecuting: state.isExecuting })
)
console.log(batcher.state.isExecuting) // Reactive value

// Multiple state properties
const batcher = useAsyncBatcher(
  asyncBatchFn,
  { maxSize: 5, wait: 1000 },
  (state) => ({
    isExecuting: state.isExecuting,
    successCount: state.successCount,
    errorCount: state.errorCount
  })
)

Anfangszustand

Sie können initiale Zustandswerte beim Erstellen eines asynchronen Batchers angeben. Dies wird häufig verwendet, um Zustände aus persistentem Speicher wiederherzustellen.

ts
// Load initial state from localStorage
const savedState = localStorage.getItem('async-batcher-state')
const initialState = savedState ? JSON.parse(savedState) : {}

const batcher = new AsyncBatcher(asyncBatchFn, {
  maxSize: 5,
  wait: 1000,
  initialState
})
// Load initial state from localStorage
const savedState = localStorage.getItem('async-batcher-state')
const initialState = savedState ? JSON.parse(savedState) : {}

const batcher = new AsyncBatcher(asyncBatchFn, {
  maxSize: 5,
  wait: 1000,
  initialState
})

Zustandsänderungen abonnieren

Der Store ist reaktiv und unterstützt Abonnements

ts
const batcher = new AsyncBatcher(asyncBatchFn, { maxSize: 5, wait: 1000 })

// Subscribe to state changes
const unsubscribe = batcher.store.subscribe((state) => {
  // do something with the state like persist it to localStorage
})

// Unsubscribe when done
unsubscribe()
const batcher = new AsyncBatcher(asyncBatchFn, { maxSize: 5, wait: 1000 })

// Subscribe to state changes
const unsubscribe = batcher.store.subscribe((state) => {
  // do something with the state like persist it to localStorage
})

// Unsubscribe when done
unsubscribe()

Hinweis: Dies ist nicht notwendig, wenn Sie einen Framework-Adapter verwenden, da der zugrunde liegende Hook useStore dies bereits erledigt. Sie können auch useStore von TanStack Store importieren und verwenden, um batcher.store.state nach Bedarf in reaktiven Zustand mit einem benutzerdefinierten Selektor umzuwandeln.

ts
const batcher = useAsyncBatcher(asyncBatchFn, { maxSize: 5, wait: 1000 })

// you could manually use the `useStore` hook to subscribe to state changes in whatever scope you want
const state = useStore(batcher.store, (state) => ({
  successCount: state.successCount,
}))

console.log(state)
const batcher = useAsyncBatcher(asyncBatchFn, { maxSize: 5, wait: 1000 })

// you could manually use the `useStore` hook to subscribe to state changes in whatever scope you want
const state = useStore(batcher.store, (state) => ({
  successCount: state.successCount,
}))

console.log(state)

Verfügbare Zustandseigenschaften

Die AsyncBatcherState beinhaltet:

  • errorCount: Anzahl der Batch-Ausführungen, die zu Fehlern geführt haben.
  • failedItems: Array von Elementen, die während der Batch-Verarbeitung fehlgeschlagen sind.
  • isEmpty: Gibt an, ob der Batcher keine zu verarbeitenden Elemente hat (Elemente-Array ist leer).
  • isExecuting: Gibt an, ob ein Batch gerade asynchron verarbeitet wird.
  • isPending: Gibt an, ob der Batcher auf den Timeout wartet, um die Stapelverarbeitung auszulösen
  • items: Array von Elementen, die derzeit zur Stapelverarbeitung in der Warteschlange stehen
  • lastResult: Das Ergebnis der letzten Batch-Ausführung.
  • settleCount: Anzahl der Batch-Ausführungen, die abgeschlossen wurden (erfolgreich oder mit Fehlern).
  • size: Anzahl der Elemente, die sich derzeit in der Stapelwarteschlange befinden
  • status: Aktueller Verarbeitungsstatus ('idle' | 'pending' | 'executing' | 'populated').
  • successCount: Anzahl der Batch-Ausführungen, die erfolgreich abgeschlossen wurden.
  • totalItemsFailed: Gesamtzahl der Elemente, die bei allen Batches fehlgeschlagen sind.
  • totalItemsProcessed: Gesamtzahl der Elemente, die bei allen Batches verarbeitet wurden.

Überwachung fehlgeschlagener Elemente

Der asynchrone Batcher verfolgt Elemente, die während der Batch-Verarbeitung fehlgeschlagen sind.

ts
const batcher = new AsyncBatcher<number>(
  async (items) => {
    // This might fail for some items
    if (items.some(item => item < 0)) {
      throw new Error('Negative numbers not allowed')
    }
    return await processBatch(items)
  },
  {
    maxSize: 3,
    onError: (error, batch, batcher) => {
      console.log('Failed batch:', batch)
      console.log('All failed items:', batcher.peekFailedItems())
    }
  }
)
const batcher = new AsyncBatcher<number>(
  async (items) => {
    // This might fail for some items
    if (items.some(item => item < 0)) {
      throw new Error('Negative numbers not allowed')
    }
    return await processBatch(items)
  },
  {
    maxSize: 3,
    onError: (error, batch, batcher) => {
      console.log('Failed batch:', batch)
      console.log('All failed items:', batcher.peekFailedItems())
    }
  }
)

Framework-Adapter

Jeder Framework-Adapter bietet Hooks, die auf der Kernfunktionalität für asynchrones Batching aufbauen, um sich in das Zustandsmanagementsystem des Frameworks zu integrieren. Hooks wie useAsyncBatcher oder ähnliche sind für jedes Framework verfügbar.


Für Kernkonzepte des Batchings und synchrones Batching siehe den Batching Guide.

Unsere Partner
Code Rabbit
Unkey
Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.

Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.