Docs
CodeRabbit
Cloudflare
AG Grid
Netlify
Neon
WorkOS
Clerk
Convex
Electric
Sentry
Prisma
Strapi
Unkey
UI.dev
CodeRabbit
Cloudflare
AG Grid
Netlify
Neon
WorkOS
Clerk
Convex
Electric
Sentry
Prisma
Strapi
Unkey
UI.dev
Debouncer API Reference
Throttler API Reference
Rate Limiter API Reference
Queue API Reference
Batcher API Reference

Solid Example: CreateAsyncBatcher

tsx
import { For, createSignal } from 'solid-js'
import { render } from 'solid-js/web'
import { createAsyncBatcher } from '@tanstack/solid-pacer/async-batcher'

const fakeProcessingTime = 1000

type Item = {
  id: number
  value: string
  timestamp: number
}

function App() {
  // Use your state management library of choice
  const [processedBatches, setProcessedBatches] = createSignal<
    Array<{ items: Array<Item>; result: string; timestamp: number }>
  >([])
  const [errors, setErrors] = createSignal<Array<string>>([])
  const [shouldFail, setShouldFail] = createSignal(false)

  // The async function that will process a batch of items
  async function processBatch(items: Array<Item>): Promise<string> {
    console.log('Processing batch of', items.length, 'items:', items)

    // Simulate async processing time
    await new Promise((resolve) => setTimeout(resolve, fakeProcessingTime))

    // Simulate occasional failures for demo purposes
    if (shouldFail() && Math.random() < 0.3) {
      throw new Error(`Processing failed for batch with ${items.length} items`)
    }

    // Return a result from the batch processing
    const result = `Processed ${items.length} items: ${items.map((item) => item.value).join(', ')}`

    setProcessedBatches((prev) => [
      ...prev,
      { items, result, timestamp: Date.now() },
    ])

    return result
  }

  const batcher = createAsyncBatcher(
    processBatch,
    {
      maxSize: 5, // Process in batches of 5 (if reached before wait time)
      wait: 2000, // Wait up to 2 seconds before processing a batch
      getShouldExecute: (items) =>
        items.some((item) => item.value.includes('urgent')), // Process immediately if any item is marked urgent
      throwOnError: false, // Don't throw errors, handle them via onError
      onSuccess: (result, batch, batcher) => {
        console.log('Batch succeeded:', result)
        console.log('Processed batch:', batch)
        console.log(
          'Total successful batches:',
          batcher.store.state.successCount,
        )
      },
      onError: (error: any, _batcher) => {
        console.error('Batch failed:', error)
        setErrors((prev) => [
          ...prev,
          `Error: ${error} (${new Date().toLocaleTimeString()})`,
        ])
      },
      onSettled: (batch, batcher) => {
        console.log('Batch settled:', batch)
        console.log(
          'Total processed items:',
          batcher.store.state.totalItemsProcessed,
        )
      },
    },
    // Alternative to batcher.Subscribe: pass a selector as 3rd arg to track state and subscribe to updates
    // (state) => state,
  )

  const addItem = (isUrgent = false) => {
    const nextId = Date.now()
    const item: Item = {
      id: nextId,
      value: isUrgent ? `urgent-${nextId}` : `item-${nextId}`,
      timestamp: nextId,
    }
    batcher.addItem(item)
  }

  const executeCurrentBatch = async () => {
    try {
      const result = await batcher.flush()
      console.log('Manual execution result:', result)
    } catch (error) {
      console.error('Manual execution failed:', error)
    }
  }

  return (
    <div>
      <h1>TanStack Pacer createAsyncBatcher Example</h1>

      <batcher.Subscribe
        selector={(state) => ({
          size: state.size,
          isExecuting: state.isExecuting,
          status: state.status,
          successCount: state.successCount,
          errorCount: state.errorCount,
          totalItemsProcessed: state.totalItemsProcessed,
          items: state.items,
        })}
      >
        {(state) => (
          <>
            <div>
              <h3>Batch Status</h3>
              <div>Current Batch Size: {state().size}</div>
              <div>Max Batch Size: 5</div>
              <div>Is Executing: {state().isExecuting ? 'Yes' : 'No'}</div>
              <div>Status: {state().status}</div>
              <div>Successful Batches: {state().successCount}</div>
              <div>Failed Batches: {state().errorCount}</div>
              <div>Total Items Processed: {state().totalItemsProcessed}</div>
            </div>

            <div>
              <h3>Current Batch Items</h3>
              <div style={{ 'min-height': '100px' }}>
                {state().items.length === 0 ? (
                  <em>No items in current batch</em>
                ) : (
                  <For each={state().items}>
                    {(item, index) => (
                      <div>
                        {index() + 1}: {item.value} (added at{' '}
                        {new Date(item.timestamp).toLocaleTimeString()})
                      </div>
                    )}
                  </For>
                )}
              </div>
            </div>

            <div>
              <h3>Controls</h3>
              <div
                style={{
                  display: 'grid',
                  'grid-template-columns': 'repeat(2, 1fr)',
                  gap: '8px',
                  'max-width': '600px',
                }}
              >
                <button onClick={() => addItem(false)}>Add Regular Item</button>
                <button onClick={() => addItem(true)}>
                  Add Urgent Item (Processes Immediately)
                </button>
                <button
                  disabled={state().size === 0 || state().isExecuting}
                  onClick={executeCurrentBatch}
                >
                  Process Current Batch Now
                </button>
                <button
                  onClick={() => batcher.clear()}
                  disabled={state().size === 0 || state().isExecuting}
                >
                  Clear Current Batch
                </button>
              </div>
            </div>
          </>
        )}
      </batcher.Subscribe>

      <div>
        <label>
          <input
            type="checkbox"
            checked={shouldFail()}
            onChange={(e) => setShouldFail(e.currentTarget.checked)}
          />{' '}
          Simulate random failures (30% chance)
        </label>
      </div>

      <div>
        <h3>Processed Batches ({processedBatches().length})</h3>
        <div>
          {processedBatches().length === 0 ? (
            <em>No batches processed yet</em>
          ) : (
            <For each={processedBatches()}>
              {(batch, index) => (
                <div>
                  <strong>Batch {index() + 1}</strong> (processed at{' '}
                  {new Date(batch.timestamp).toLocaleTimeString()})
                  <div>{batch.result}</div>
                </div>
              )}
            </For>
          )}
        </div>
      </div>

      {errors().length > 0 && (
        <div>
          <h3>Errors ({errors().length})</h3>
          <div>
            <For each={errors()}>
              {(error, index) => (
                <div>
                  {index() + 1}: {error}
                </div>
              )}
            </For>
          </div>
          <button onClick={() => setErrors([])}>Clear Errors</button>
        </div>
      )}
      <batcher.Subscribe selector={(state) => state}>
        {(state) => (
          <pre style={{ 'margin-top': '20px' }}>
            {JSON.stringify(state, null, 2)}
          </pre>
        )}
      </batcher.Subscribe>
    </div>
  )
}

render(() => <App />, document.getElementById('root')!)
import { For, createSignal } from 'solid-js'
import { render } from 'solid-js/web'
import { createAsyncBatcher } from '@tanstack/solid-pacer/async-batcher'

const fakeProcessingTime = 1000

type Item = {
  id: number
  value: string
  timestamp: number
}

function App() {
  // Use your state management library of choice
  const [processedBatches, setProcessedBatches] = createSignal<
    Array<{ items: Array<Item>; result: string; timestamp: number }>
  >([])
  const [errors, setErrors] = createSignal<Array<string>>([])
  const [shouldFail, setShouldFail] = createSignal(false)

  // The async function that will process a batch of items
  async function processBatch(items: Array<Item>): Promise<string> {
    console.log('Processing batch of', items.length, 'items:', items)

    // Simulate async processing time
    await new Promise((resolve) => setTimeout(resolve, fakeProcessingTime))

    // Simulate occasional failures for demo purposes
    if (shouldFail() && Math.random() < 0.3) {
      throw new Error(`Processing failed for batch with ${items.length} items`)
    }

    // Return a result from the batch processing
    const result = `Processed ${items.length} items: ${items.map((item) => item.value).join(', ')}`

    setProcessedBatches((prev) => [
      ...prev,
      { items, result, timestamp: Date.now() },
    ])

    return result
  }

  const batcher = createAsyncBatcher(
    processBatch,
    {
      maxSize: 5, // Process in batches of 5 (if reached before wait time)
      wait: 2000, // Wait up to 2 seconds before processing a batch
      getShouldExecute: (items) =>
        items.some((item) => item.value.includes('urgent')), // Process immediately if any item is marked urgent
      throwOnError: false, // Don't throw errors, handle them via onError
      onSuccess: (result, batch, batcher) => {
        console.log('Batch succeeded:', result)
        console.log('Processed batch:', batch)
        console.log(
          'Total successful batches:',
          batcher.store.state.successCount,
        )
      },
      onError: (error: any, _batcher) => {
        console.error('Batch failed:', error)
        setErrors((prev) => [
          ...prev,
          `Error: ${error} (${new Date().toLocaleTimeString()})`,
        ])
      },
      onSettled: (batch, batcher) => {
        console.log('Batch settled:', batch)
        console.log(
          'Total processed items:',
          batcher.store.state.totalItemsProcessed,
        )
      },
    },
    // Alternative to batcher.Subscribe: pass a selector as 3rd arg to track state and subscribe to updates
    // (state) => state,
  )

  const addItem = (isUrgent = false) => {
    const nextId = Date.now()
    const item: Item = {
      id: nextId,
      value: isUrgent ? `urgent-${nextId}` : `item-${nextId}`,
      timestamp: nextId,
    }
    batcher.addItem(item)
  }

  const executeCurrentBatch = async () => {
    try {
      const result = await batcher.flush()
      console.log('Manual execution result:', result)
    } catch (error) {
      console.error('Manual execution failed:', error)
    }
  }

  return (
    <div>
      <h1>TanStack Pacer createAsyncBatcher Example</h1>

      <batcher.Subscribe
        selector={(state) => ({
          size: state.size,
          isExecuting: state.isExecuting,
          status: state.status,
          successCount: state.successCount,
          errorCount: state.errorCount,
          totalItemsProcessed: state.totalItemsProcessed,
          items: state.items,
        })}
      >
        {(state) => (
          <>
            <div>
              <h3>Batch Status</h3>
              <div>Current Batch Size: {state().size}</div>
              <div>Max Batch Size: 5</div>
              <div>Is Executing: {state().isExecuting ? 'Yes' : 'No'}</div>
              <div>Status: {state().status}</div>
              <div>Successful Batches: {state().successCount}</div>
              <div>Failed Batches: {state().errorCount}</div>
              <div>Total Items Processed: {state().totalItemsProcessed}</div>
            </div>

            <div>
              <h3>Current Batch Items</h3>
              <div style={{ 'min-height': '100px' }}>
                {state().items.length === 0 ? (
                  <em>No items in current batch</em>
                ) : (
                  <For each={state().items}>
                    {(item, index) => (
                      <div>
                        {index() + 1}: {item.value} (added at{' '}
                        {new Date(item.timestamp).toLocaleTimeString()})
                      </div>
                    )}
                  </For>
                )}
              </div>
            </div>

            <div>
              <h3>Controls</h3>
              <div
                style={{
                  display: 'grid',
                  'grid-template-columns': 'repeat(2, 1fr)',
                  gap: '8px',
                  'max-width': '600px',
                }}
              >
                <button onClick={() => addItem(false)}>Add Regular Item</button>
                <button onClick={() => addItem(true)}>
                  Add Urgent Item (Processes Immediately)
                </button>
                <button
                  disabled={state().size === 0 || state().isExecuting}
                  onClick={executeCurrentBatch}
                >
                  Process Current Batch Now
                </button>
                <button
                  onClick={() => batcher.clear()}
                  disabled={state().size === 0 || state().isExecuting}
                >
                  Clear Current Batch
                </button>
              </div>
            </div>
          </>
        )}
      </batcher.Subscribe>

      <div>
        <label>
          <input
            type="checkbox"
            checked={shouldFail()}
            onChange={(e) => setShouldFail(e.currentTarget.checked)}
          />{' '}
          Simulate random failures (30% chance)
        </label>
      </div>

      <div>
        <h3>Processed Batches ({processedBatches().length})</h3>
        <div>
          {processedBatches().length === 0 ? (
            <em>No batches processed yet</em>
          ) : (
            <For each={processedBatches()}>
              {(batch, index) => (
                <div>
                  <strong>Batch {index() + 1}</strong> (processed at{' '}
                  {new Date(batch.timestamp).toLocaleTimeString()})
                  <div>{batch.result}</div>
                </div>
              )}
            </For>
          )}
        </div>
      </div>

      {errors().length > 0 && (
        <div>
          <h3>Errors ({errors().length})</h3>
          <div>
            <For each={errors()}>
              {(error, index) => (
                <div>
                  {index() + 1}: {error}
                </div>
              )}
            </For>
          </div>
          <button onClick={() => setErrors([])}>Clear Errors</button>
        </div>
      )}
      <batcher.Subscribe selector={(state) => state}>
        {(state) => (
          <pre style={{ 'margin-top': '20px' }}>
            {JSON.stringify(state, null, 2)}
          </pre>
        )}
      </batcher.Subscribe>
    </div>
  )
}

render(() => <App />, document.getElementById('root')!)