queueQueuerIm Gegensatz zu Ratenbegrenzung, Drosselung und Debouncing, die Ausführungen verwerfen, wenn sie zu häufig auftreten, können Queues so konfiguriert werden, dass sichergestellt wird, dass jede Operation verarbeitet wird. Sie bieten eine Möglichkeit, den Fluss von Operationen zu verwalten und zu steuern, ohne Anfragen zu verlieren. Dies macht sie ideal für Szenarien, in denen Datenverlust inakzeptabel ist. Warteschlangen können auch auf eine maximale Größe eingestellt werden, was nützlich sein kann, um Speicherlecks oder andere Probleme zu verhindern. Dieser Leitfaden behandelt die Konzepte von Warteschlangen in TanStack Pacer.
Warteschlangen stellen sicher, dass jede Operation schließlich verarbeitet wird, auch wenn sie schneller eingehen, als sie bearbeitet werden können. Im Gegensatz zu anderen Techniken zur Ausführungskontrolle, die überschüssige Operationen verwerfen, puffern Warteschlangen Operationen in einer geordneten Liste und verarbeiten sie gemäß spezifischen Regeln. Dies macht die Warteschlange zur einzigen "verlustfreien" Technik zur Ausführungskontrolle in TanStack Pacer, es sei denn, eine maxSize wird angegeben, was dazu führen kann, dass Elemente abgelehnt werden, wenn der Puffer voll ist.
Queuing (processing one item every 2 ticks)
Timeline: [1 second per tick]
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Queue: [ABC] [BC] [BCDE] [DE] [E] []
Executed: ✅ ✅ ✅ ✅ ✅ ✅
[=================================================================]
^ Unlike rate limiting/throttling/debouncing,
ALL calls are eventually processed in order
[Items queue up] [Process steadily] [Empty]
when busy one by one queue
Queuing (processing one item every 2 ticks)
Timeline: [1 second per tick]
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Queue: [ABC] [BC] [BCDE] [DE] [E] []
Executed: ✅ ✅ ✅ ✅ ✅ ✅
[=================================================================]
^ Unlike rate limiting/throttling/debouncing,
ALL calls are eventually processed in order
[Items queue up] [Process steadily] [Empty]
when busy one by one queue
Warteschlangen sind besonders wichtig, wenn Sie sicherstellen müssen, dass jede Operation verarbeitet wird, auch wenn dies eine gewisse Verzögerung bedeutet. Dies macht sie ideal für Szenarien, in denen Datenkonsistenz und Vollständigkeit wichtiger sind als die sofortige Ausführung. Bei Verwendung einer maxSize kann sie auch als Puffer dienen, um zu verhindern, dass ein System mit zu vielen ausstehenden Operationen überlastet wird.
Warteschlangen sind möglicherweise nicht die beste Wahl, wenn
Tipp
Wenn Sie derzeit Ratenbegrenzung, Drosselung oder Debouncing verwenden, aber feststellen, dass verworfene Operationen Probleme verursachen, sind Warteschlangen wahrscheinlich die Lösung, die Sie benötigen.
TanStack Pacer bietet Warteschlangen über die einfache Funktion queue und die leistungsfähigere Klasse Queuer. Während andere Techniken zur Ausführungskontrolle typischerweise ihre funktionsbasierten APIs bevorzugen, profitieren Warteschlangen oft von der zusätzlichen Kontrolle, die die klassenbasierte API bietet.
Die Funktion queue bietet eine einfache Möglichkeit, eine ständig laufende Warteschlange zu erstellen, die Elemente verarbeitet, sobald sie hinzugefügt werden.
import { queue } from '@tanstack/pacer'
// Create a queue that processes items every second
const processItems = queue<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
wait: 1000,
maxSize: 10, // Optional: limit queue size to prevent memory or time issues
onItemsChange: (queuer) => {
console.log('Current queue:', queuer.peekAllItems())
}
}
)
// Add items to be processed
processItems(1) // Processed immediately
processItems(2) // Processed after 1 second
processItems(3) // Processed after 2 seconds
import { queue } from '@tanstack/pacer'
// Create a queue that processes items every second
const processItems = queue<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
wait: 1000,
maxSize: 10, // Optional: limit queue size to prevent memory or time issues
onItemsChange: (queuer) => {
console.log('Current queue:', queuer.peekAllItems())
}
}
)
// Add items to be processed
processItems(1) // Processed immediately
processItems(2) // Processed after 1 second
processItems(3) // Processed after 2 seconds
Obwohl die Funktion queue einfach zu verwenden ist, bietet sie nur eine grundlegende, ständig laufende Warteschlange über die Methode addItem. Für die meisten Anwendungsfälle wünschen Sie die zusätzlichen Kontrollen und Funktionen, die die Klasse Queuer bietet.
Die Klasse Queuer bietet die vollständige Kontrolle über das Verhalten und die Verarbeitung der Warteschlange.
import { Queuer } from '@tanstack/pacer'
// Create a queue that processes items every second
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
wait: 1000, // Wait 1 second between processing items
maxSize: 5, // Optional: limit queue size to prevent memory or time issues
onItemsChange: (queuer) => {
console.log('Current queue:', queuer.peekAllItems())
}
}
)
// Start processing
queue.start()
// Add items to be processed
queue.addItem(1)
queue.addItem(2)
queue.addItem(3)
// Items will be processed one at a time with 1 second delay between each
// Output:
// Processing: 1 (immediately)
// Processing: 2 (after 1 second)
// Processing: 3 (after 2 seconds)
import { Queuer } from '@tanstack/pacer'
// Create a queue that processes items every second
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
wait: 1000, // Wait 1 second between processing items
maxSize: 5, // Optional: limit queue size to prevent memory or time issues
onItemsChange: (queuer) => {
console.log('Current queue:', queuer.peekAllItems())
}
}
)
// Start processing
queue.start()
// Add items to be processed
queue.addItem(1)
queue.addItem(2)
queue.addItem(3)
// Items will be processed one at a time with 1 second delay between each
// Output:
// Processing: 1 (immediately)
// Processing: 2 (after 1 second)
// Processing: 3 (after 2 seconds)
Was TanStack Pacers Queuer einzigartig macht, ist seine Fähigkeit, sich durch seine positionsbasierte API an verschiedene Anwendungsfälle anzupassen. Derselbe Queuer kann als traditionelle Warteschlange, ein Stapel oder eine doppelseitige Warteschlange fungieren, alles über dieselbe konsistente Schnittstelle.
Das Standardverhalten, bei dem Elemente in der Reihenfolge ihrer Hinzufügung verarbeitet werden. Dies ist der gängigste Warteschlangentyp und folgt dem Prinzip, dass das zuerst hinzugefügte Element zuerst verarbeitet werden sollte. Bei Verwendung von maxSize werden neue Elemente abgelehnt, wenn die Warteschlange voll ist.
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
addItemsTo: 'back', // default
getItemsFrom: 'front', // default
}
)
queue.addItem(1) // [1]
queue.addItem(2) // [1, 2]
// Processes: 1, then 2
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
addItemsTo: 'back', // default
getItemsFrom: 'front', // default
}
)
queue.addItem(1) // [1]
queue.addItem(2) // [1, 2]
// Processes: 1, then 2
Durch die Angabe von 'back' als Position sowohl für das Hinzufügen als auch für das Abrufen von Elementen verhält sich der Queuer wie ein Stapel. In einem Stapel ist das zuletzt hinzugefügte Element das erste, das verarbeitet wird. Bei Verwendung von maxSize werden neue Elemente abgelehnt, wenn der Stapel voll ist.
const stack = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
addItemsTo: 'back', // default
getItemsFrom: 'back', // override default for stack behavior
}
)
stack.addItem(1) // [1]
stack.addItem(2) // [1, 2]
// Items will process in order: 2, then 1
stack.getNextItem('back') // get next item from back of queue instead of front
const stack = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
addItemsTo: 'back', // default
getItemsFrom: 'back', // override default for stack behavior
}
)
stack.addItem(1) // [1]
stack.addItem(2) // [1, 2]
// Items will process in order: 2, then 1
stack.getNextItem('back') // get next item from back of queue instead of front
Prioritätswarteschlangen fügen eine weitere Dimension zur Reihenfolge von Warteschlangen hinzu, indem sie es ermöglichen, Elemente nach ihrer Priorität und nicht nur nach ihrer Einfügungsreihenfolge zu sortieren. Jedes Element erhält einen Prioritätswert, und die Warteschlange ordnet die Elemente automatisch nach Priorität. Bei Verwendung von maxSize können niedrigere Prioritätselemente abgelehnt werden, wenn die Warteschlange voll ist.
const priorityQueue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
getPriority: (n) => n // Higher numbers have priority
}
)
priorityQueue.addItem(1) // [1]
priorityQueue.addItem(3) // [3, 1]
priorityQueue.addItem(2) // [3, 2, 1]
// Processes: 3, 2, then 1
const priorityQueue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
getPriority: (n) => n // Higher numbers have priority
}
)
priorityQueue.addItem(1) // [1]
priorityQueue.addItem(3) // [3, 1]
priorityQueue.addItem(2) // [3, 2, 1]
// Processes: 3, 2, then 1
Die Klasse Queuer unterstützt das Starten und Stoppen der Verarbeitung über die Methoden start() und stop(). Standardmäßig beginnen Warteschlangen automatisch mit der Verarbeitung. Sie können started: false setzen, um die Warteschlange zunächst pausiert zu lassen, was es Ihnen ermöglicht, entweder
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
started: false // Start paused
}
)
queue.start() // Begin processing items
queue.stop() // Pause processing
// Manually process items while the queue is stopped (run it your own way)
queue.getNextItem() // Get next item
queue.getNextItem() // Get next item
queue.getNextItem() // Get next item
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
started: false // Start paused
}
)
queue.start() // Begin processing items
queue.stop() // Pause processing
// Manually process items while the queue is stopped (run it your own way)
queue.getNextItem() // Get next item
queue.getNextItem() // Get next item
queue.getNextItem() // Get next item
Die Queuer-Klasse bietet mehrere hilfreiche Methoden zur Verwaltung von Warteschlangen.
// Queue inspection
queue.peekNextItem() // View next item without removing it
queue.store.state.size // Get current queue size
queue.store.state.isEmpty // Check if queue is empty
queue.store.state.isFull // Check if queue has reached maxSize
queue.peekAllItems() // Get copy of all queued items
// Queue manipulation
queue.clear() // Remove all items
queue.reset() // Reset to initial state
queue.store.state.executionCount // Get number of processed items
queue.flush() // Flush all pending items immediately
// Event handling (use the onItemsChange option, not a method)
// Example:
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
onItemsChange: (queuer) => {
console.log('Processed:', queuer.peekAllItems())
}
}
)
// Queue inspection
queue.peekNextItem() // View next item without removing it
queue.store.state.size // Get current queue size
queue.store.state.isEmpty // Check if queue is empty
queue.store.state.isFull // Check if queue has reached maxSize
queue.peekAllItems() // Get copy of all queued items
// Queue manipulation
queue.clear() // Remove all items
queue.reset() // Reset to initial state
queue.store.state.executionCount // Get number of processed items
queue.flush() // Flush all pending items immediately
// Event handling (use the onItemsChange option, not a method)
// Example:
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
onItemsChange: (queuer) => {
console.log('Processed:', queuer.peekAllItems())
}
}
)
Die Queuer-Klasse unterstützt den automatischen Ablauf von Elementen, die sich zu lange in der Warteschlange befinden. Dies ist nützlich, um die Verarbeitung veralteter Daten zu verhindern oder Timeouts für Warteschlangenoperationen zu implementieren.
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
expirationDuration: 5000, // Items expire after 5 seconds
onExpire: (item, queuer) => {
console.log('Item expired:', item)
}
}
)
// Or use a custom expiration check
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
getIsExpired: (item, addedAt) => {
// Custom expiration logic
return Date.now() - addedAt > 5000
},
onExpire: (item, queuer) => {
console.log('Item expired:', item)
}
}
)
// Check expiration statistics
console.log(queue.store.state.expirationCount) // Number of items that have expired
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
expirationDuration: 5000, // Items expire after 5 seconds
onExpire: (item, queuer) => {
console.log('Item expired:', item)
}
}
)
// Or use a custom expiration check
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
getIsExpired: (item, addedAt) => {
// Custom expiration logic
return Date.now() - addedAt > 5000
},
onExpire: (item, queuer) => {
console.log('Item expired:', item)
}
}
)
// Check expiration statistics
console.log(queue.store.state.expirationCount) // Number of items that have expired
Ablauffunktionen sind besonders nützlich für
Wenn eine Warteschlange ihre maximale Größe erreicht (festgelegt durch die Option maxSize), werden neue Elemente abgelehnt. Die Queuer bietet Möglichkeiten, diese Ablehnungen zu handhaben und zu überwachen.
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
maxSize: 2, // Only allow 2 items in queue
onReject: (item, queuer) => {
console.log('Queue is full. Item rejected:', item)
}
}
)
queue.addItem(1) // Accepted
queue.addItem(2) // Accepted
queue.addItem(3) // Rejected, triggers onReject callback
console.log(queue.store.state.rejectionCount) // 1
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
maxSize: 2, // Only allow 2 items in queue
onReject: (item, queuer) => {
console.log('Queue is full. Item rejected:', item)
}
}
)
queue.addItem(1) // Accepted
queue.addItem(2) // Accepted
queue.addItem(3) // Rejected, triggers onReject callback
console.log(queue.store.state.rejectionCount) // 1
Sie können eine Warteschlange beim Erstellen mit anfänglichen Elementen vorbefüllen.
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
initialItems: [1, 2, 3],
started: true // Start processing immediately
}
)
// Queue starts with [1, 2, 3] and begins processing
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
initialItems: [1, 2, 3],
started: true // Start processing immediately
}
)
// Queue starts with [1, 2, 3] and begins processing
Die Optionen des Queuers können nach der Erstellung mit setOptions() geändert werden. Darüber hinaus unterstützen mehrere Optionen dynamische Werte über Callback-Funktionen.
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
wait: 1000,
started: false
}
)
// Change configuration
queue.setOptions({
wait: 500, // Process items twice as fast
started: true // Start processing
})
// Access current state
console.log(queue.store.state.size) // Current queue size
console.log(queue.store.state.isRunning) // Whether queue is running
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
wait: 1000,
started: false
}
)
// Change configuration
queue.setOptions({
wait: 500, // Process items twice as fast
started: true // Start processing
})
// Access current state
console.log(queue.store.state.size) // Current queue size
console.log(queue.store.state.isRunning) // Whether queue is running
Mehrere Optionen im Queuer unterstützen dynamische Werte durch Callback-Funktionen, die die Queuer-Instanz erhalten.
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
// Dynamic wait time based on queue size
wait: (queuer) => {
return queuer.store.state.size > 10 ? 2000 : 1000
}
}
)
const queue = new Queuer<number>(
(item) => {
// Process each item
console.log('Processing:', item)
},
{
// Dynamic wait time based on queue size
wait: (queuer) => {
return queuer.store.state.size > 10 ? 2000 : 1000
}
}
)
Die folgenden Optionen unterstützen dynamische Werte
Dies ermöglicht ein anspruchsvolles Warteschlangenverhalten, das sich an Laufzeitbedingungen anpasst.
Der Queuer unterstützt das Leeren von Elementen zur sofortigen Verarbeitung.
const queue = new Queuer(processFn, { wait: 5000 })
queue.addItem('item1')
queue.addItem('item2')
console.log(queue.store.state.size) // 2
// Flush all items immediately instead of waiting
queue.flush()
console.log(queue.store.state.size) // 0 (items were processed)
// Or flush a specific number of items
queue.addItem('item3')
queue.addItem('item4')
queue.addItem('item5')
queue.flush(2) // Process only 2 items
console.log(queue.store.state.size) // 1 (one item remaining)
const queue = new Queuer(processFn, { wait: 5000 })
queue.addItem('item1')
queue.addItem('item2')
console.log(queue.store.state.size) // 2
// Flush all items immediately instead of waiting
queue.flush()
console.log(queue.store.state.size) // 0 (items were processed)
// Or flush a specific number of items
queue.addItem('item3')
queue.addItem('item4')
queue.addItem('item5')
queue.flush(2) // Process only 2 items
console.log(queue.store.state.size) // 1 (one item remaining)
Die Klasse Queuer verwendet TanStack Store für reaktives Zustandsmanagement und bietet Echtzeit-Zugriff auf den Warteschlangenstatus, Verarbeitungsstatistiken und die Verfolgung gleichzeitiger Aufgaben. Der gesamte Zustand wird in einem TanStack Store gespeichert und kann über queuer.store.state abgerufen werden. Wenn Sie jedoch einen Framework-Adapter wie React oder Solid verwenden, sollten Sie den Zustand nicht von hier abrufen. Stattdessen lesen Sie den Zustand von queuer.state und stellen eine Selector-Callback als 3. Argument für den Hook useQueuer bereit, um sich für die Zustandsverfolgung zu entscheiden, wie unten gezeigt.
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 queuer.state leer ({}), da der Selektor standardmäßig leer ist. Hier wird der reaktive Zustand eines TanStack Stores useStore gespeichert. Sie müssen sich für die Zustandsverfolgung entscheiden, indem Sie eine Selektorfunktion bereitstellen.
// Default behavior - no reactive state subscriptions
const queue = useQueuer(processFn, { wait: 1000, maxSize: 10 })
console.log(queue.state) // {}
// Opt-in to re-render when size changes
const queue = useQueuer(
processFn,
{ wait: 1000, maxSize: 10 },
(state) => ({ size: state.size })
)
console.log(queue.state.size) // Reactive value
// Multiple state properties
const queue = useQueuer(
processFn,
{ wait: 1000, maxSize: 10 },
(state) => ({
size: state.size,
executionCount: state.executionCount,
status: state.status
})
)
// Default behavior - no reactive state subscriptions
const queue = useQueuer(processFn, { wait: 1000, maxSize: 10 })
console.log(queue.state) // {}
// Opt-in to re-render when size changes
const queue = useQueuer(
processFn,
{ wait: 1000, maxSize: 10 },
(state) => ({ size: state.size })
)
console.log(queue.state.size) // Reactive value
// Multiple state properties
const queue = useQueuer(
processFn,
{ wait: 1000, maxSize: 10 },
(state) => ({
size: state.size,
executionCount: state.executionCount,
status: state.status
})
)
Sie können anfängliche Zustandswerte angeben, wenn Sie einen Queuer erstellen. Dies wird üblicherweise verwendet, um den Zustand aus persistentem Speicher wiederherzustellen.
// Load initial state from localStorage
const savedState = localStorage.getItem('queuer-state')
const initialState = savedState ? JSON.parse(savedState) : {}
const queue = new Queuer(processFn, {
wait: 1000,
maxSize: 10,
initialState
})
// Load initial state from localStorage
const savedState = localStorage.getItem('queuer-state')
const initialState = savedState ? JSON.parse(savedState) : {}
const queue = new Queuer(processFn, {
wait: 1000,
maxSize: 10,
initialState
})
Der Store ist reaktiv und unterstützt Abonnements
const queue = new Queuer(processFn, { wait: 1000, maxSize: 10 })
// Subscribe to state changes
const unsubscribe = queue.store.subscribe((state) => {
// do something with the state like persist it to localStorage
})
// Unsubscribe when done
unsubscribe()
const queue = new Queuer(processFn, { wait: 1000, maxSize: 10 })
// Subscribe to state changes
const unsubscribe = queue.store.subscribe((state) => {
// do something with the state like persist it to localStorage
})
// Unsubscribe when done
unsubscribe()
Hinweis: Dies ist unnötig, wenn Sie einen Framework-Adapter verwenden, da der zugrunde liegende Hook useStore dies bereits tut. Sie können useStore auch von TanStack Store importieren und verwenden, um queuer.store.state nach Bedarf in einen reaktiven Zustand mit einem benutzerdefinierten Selektor umzuwandeln.
Die QueuerState beinhaltet
Jeder Framework-Adapter baut praktische Hooks und Funktionen um die Queuer-Klassen herum. Hooks wie useQueuer, useQueuedState und useQueuedValue sind kleine Wrapper, die den Aufwand für gängige Anwendungsfälle in Ihrem Code reduzieren können.
Für asynchrone Warteschlangen siehe den Leitfaden zu asynchronen Warteschlangen.
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.