Store
The Store class represents a resource that holds distinct JavaScript objects. Unlike Buffer which tracks homogeneous quantities, Store manages a collection of individual items with optional filter-based retrieval. Perfect for modeling warehouses, parking lots, job queues, or any system managing distinct entities.
Constructor
new Store<T>(sim: Simulation, capacity: number, options?: StoreOptions)Type Parameters:
T- The type of items stored (can be any JavaScript object)
Parameters:
sim- Simulation instancecapacity- Maximum number of items the store can hold (must be > 0)options- Optional configuration objectname- Store name for statistics and debugging
Example:
import { Simulation, Store } from 'discrete-sim';
const sim = new Simulation();
// Warehouse storing pallets
interface Pallet {
id: string;
destination: string;
weight: number;
}
const warehouse = new Store<Pallet>(sim, 100, {
name: 'Warehouse'
});
// Parking lot storing cars
interface Car {
licensePlate: string;
arrivalTime: number;
}
const parkingLot = new Store<Car>(sim, 50, {
name: 'Parking Lot'
});Validation:
capacitymust be > 0 and finite- Throws
ValidationErrorfor invalid capacity
Properties
capacity
store.capacity: numberMaximum number of items the store can hold.
Example:
const warehouse = new Store(sim, 100);
console.log(warehouse.capacity); // 100Read-only - Cannot be changed after creation.
size
store.size: numberCurrent number of items in the store.
Example:
console.log(`${warehouse.size}/${warehouse.capacity} spaces used`);Range: 0 to capacity
available
store.available: numberAvailable space in the store.
Example:
if (warehouse.available > 0) {
console.log('Space available for new items');
}Relation: available = capacity - size
items
store.items: ReadonlyArray<T>Read-only view of items currently in the store.
Example:
console.log(`Items in warehouse:`);
warehouse.items.forEach(item => {
console.log(` - ${item.id}: ${item.destination}`);
});Note: Returns a shallow copy. Modifying the items themselves will affect the store.
putQueueLength
store.putQueueLength: numberNumber of processes waiting to put items into the store.
Example:
console.log(`${warehouse.putQueueLength} trucks waiting to unload`);getQueueLength
store.getQueueLength: numberNumber of processes waiting to get items from the store.
Example:
console.log(`${warehouse.getQueueLength} trucks waiting to load`);name
store.name: stringName of the store (for statistics and debugging).
stats
store.stats: StoreStatisticsComprehensive statistics for this store.
Returns:
interface StoreStatistics {
totalPuts: number; // Total put operations
totalGets: number; // Total get operations
averagePutWaitTime: number; // Average time waiting to put
averageGetWaitTime: number; // Average time waiting to get
averageSize: number; // Time-weighted average size
averagePutQueueLength: number;
averageGetQueueLength: number;
}Example:
const stats = warehouse.stats;
console.log(`Average occupancy: ${stats.averageSize.toFixed(2)} pallets`);
console.log(`Total items stored: ${stats.totalPuts}`);
console.log(`Total items retrieved: ${stats.totalGets}`);Methods
put()
yield store.put(item: T): voidRequest to put an item into the store. Waits if store is full.
Parameters:
item- Object to store (cannot be null or undefined)
Returns: Token to yield in generator function
Example:
function* receivePallet(pallet: Pallet) {
console.log(`Receiving pallet ${pallet.id}`);
yield warehouse.put(pallet);
console.log(`Pallet ${pallet.id} stored at ${sim.now}`);
}Behavior:
- If space available: Item stored immediately, process continues
- If store full: Process queued until space becomes available
- Must use
yield(notyield*) when putting - Throws error if item is null or undefined
Queue Discipline: FIFO (First-In-First-Out)
get()
yield store.get(filter?: (item: T) => boolean): StoreGetRequest<T>Request to get an item from the store. Waits if no matching item available.
Parameters:
filter- Optional predicate function to select specific items. If omitted, returns first item (FIFO).
Returns: StoreGetRequest token to yield. After yielding, access the retrieved item via request.retrievedItem.
Example:
// FIFO retrieval (no filter)
function* shipNext() {
const request = warehouse.get();
yield request;
const pallet = request.retrievedItem!;
console.log(`Shipping pallet ${pallet.id}`);
}
// Filtered retrieval
function* shipToNYC() {
const request = warehouse.get(p => p.destination === 'NYC');
yield request;
const pallet = request.retrievedItem!;
console.log(`Shipping pallet ${pallet.id} to NYC`);
}Behavior:
- If matching item available: Item retrieved immediately, process continues
- If no match: Process queued until matching item becomes available
- Must use
yield(notyield*) when getting - Filter function is called on each item until a match is found
Queue Discipline: FIFO for queued get requests. Filter-based requests are fulfilled as matching items become available.
Examples
Example: Warehouse with Pallets
import { Simulation, Store, timeout } from 'discrete-sim';
interface Pallet {
id: string;
destination: string;
weight: number;
}
const sim = new Simulation();
const warehouse = new Store<Pallet>(sim, 100, { name: 'Warehouse' });
// Receiving pallets
function* receiveTruck(pallets: Pallet[]) {
console.log(`[${sim.now}] Truck arriving with ${pallets.length} pallets`);
for (const pallet of pallets) {
yield warehouse.put(pallet);
console.log(`[${sim.now}] Stored pallet ${pallet.id} for ${pallet.destination}`);
yield* timeout(2); // Unloading time
}
console.log(`[${sim.now}] Truck unloaded`);
}
// Shipping to specific destination
function* shipTo(destination: string) {
console.log(`[${sim.now}] Looking for pallet to ${destination}`);
const request = warehouse.get(p => p.destination === destination);
yield request;
const pallet = request.retrievedItem!;
console.log(`[${sim.now}] Found pallet ${pallet.id}, loading truck`);
yield* timeout(5); // Loading time
console.log(`[${sim.now}] Shipped pallet ${pallet.id} to ${destination}`);
}
// Receive some pallets
const incomingPallets: Pallet[] = [
{ id: 'P001', destination: 'NYC', weight: 500 },
{ id: 'P002', destination: 'LAX', weight: 600 },
{ id: 'P003', destination: 'NYC', weight: 450 },
];
sim.process(() => receiveTruck(incomingPallets));
// Ship pallets
sim.schedule(10, () => sim.process(() => shipTo('NYC')));
sim.schedule(20, () => sim.process(() => shipTo('LAX')));
sim.run(100);
// Statistics
const stats = warehouse.stats;
console.log(`\nWarehouse Statistics:`);
console.log(`Average occupancy: ${stats.averageSize.toFixed(2)} pallets`);
console.log(`Total received: ${stats.totalPuts} pallets`);
console.log(`Total shipped: ${stats.totalGets} pallets`);
console.log(`Remaining: ${warehouse.size} pallets`);Example: Parking Lot
interface Car {
licensePlate: string;
arrivalTime: number;
owner: string;
}
const parkingLot = new Store<Car>(sim, 50, { name: 'Parking' });
function* parkCar(car: Car) {
car.arrivalTime = sim.now;
console.log(`[${sim.now}] ${car.owner} parking ${car.licensePlate}`);
yield parkingLot.put(car);
console.log(`[${sim.now}] Car parked. ${parkingLot.available} spots left`);
}
function* retrieveCar(licensePlate: string) {
console.log(`[${sim.now}] Looking for car ${licensePlate}`);
const request = parkingLot.get(c => c.licensePlate === licensePlate);
yield request;
const car = request.retrievedItem!;
const parkedTime = sim.now - car.arrivalTime;
console.log(`[${sim.now}] Found car. Parked for ${parkedTime} minutes`);
}
// Cars arriving
sim.process(() => parkCar({ licensePlate: 'ABC123', owner: 'Alice', arrivalTime: 0 }));
sim.process(() => parkCar({ licensePlate: 'XYZ789', owner: 'Bob', arrivalTime: 0 }));
// Cars leaving
sim.schedule(60, () => sim.process(() => retrieveCar('ABC123')));
sim.schedule(90, () => sim.process(() => retrieveCar('XYZ789')));
sim.run(120);Example: Job Queue with Priorities
interface Job {
id: string;
priority: number;
data: string;
}
const jobQueue = new Store<Job>(sim, 100, { name: 'Jobs' });
function* submitJob(job: Job) {
console.log(`[${sim.now}] Submitting job ${job.id} (priority ${job.priority})`);
yield jobQueue.put(job);
}
function* processHighPriorityJobs() {
while (true) {
// Get highest priority job (priority 1)
const request = jobQueue.get(j => j.priority === 1);
yield request;
const job = request.retrievedItem!;
console.log(`[${sim.now}] Processing high-priority job ${job.id}`);
yield* timeout(10);
}
}
function* processNormalJobs() {
while (true) {
// Get normal priority jobs
const request = jobQueue.get(j => j.priority === 2);
yield request;
const job = request.retrievedItem!;
console.log(`[${sim.now}] Processing normal job ${job.id}`);
yield* timeout(15);
}
}
// Submit jobs
sim.process(() => submitJob({ id: 'J1', priority: 2, data: 'Normal' }));
sim.process(() => submitJob({ id: 'J2', priority: 1, data: 'Urgent' }));
sim.process(() => submitJob({ id: 'J3', priority: 2, data: 'Normal' }));
// Start workers
sim.process(() => processHighPriorityJobs());
sim.process(() => processNormalJobs());
sim.run(100);Common Patterns
Pattern: FIFO Queue
// Put items
function* addItem(item: T) {
yield store.put(item);
}
// Get items in order
function* processNext() {
const request = store.get(); // No filter = FIFO
yield request;
const item = request.retrievedItem!;
// Process item
}Pattern: Filter by Property
// Get specific item
function* getByDestination(dest: string) {
const request = store.get(item => item.destination === dest);
yield request;
const item = request.retrievedItem!;
}
// Get by multiple criteria
function* getHeavyToNYC() {
const request = store.get(p =>
p.destination === 'NYC' && p.weight > 500
);
yield request;
const pallet = request.retrievedItem!;
}Pattern: Inspect Items Before Getting
function* selectBest() {
// Check available items
console.log('Available items:');
store.items.forEach(item => {
console.log(` ${item.id}: ${item.priority}`);
});
// Get highest priority
const maxPriority = Math.max(...store.items.map(i => i.priority));
const request = store.get(item => item.priority === maxPriority);
yield request;
const best = request.retrievedItem!;
}Pattern: Coordinated Store and Resource
function* loadTruck(truck: Resource, warehouse: Store<Pallet>) {
yield truck.request(); // Get truck
const request = warehouse.get(); // Get pallet
yield request;
const pallet = request.retrievedItem!;
yield* timeout(10); // Loading time
truck.release();
// Drive away with pallet
console.log(`Truck loaded with ${pallet.id}`);
}Best Practices
1. Use TypeScript Types
// Good - type safety
interface Part {
id: string;
type: string;
}
const store = new Store<Part>(sim, 100);
// Avoid - no type checking
const store = new Store(sim, 100); // Store<any>2. Always Check retrievedItem
// Good - safe access
const request = store.get(filter);
yield request;
const item = request.retrievedItem!;
// Or with null check
if (request.retrievedItem) {
const item = request.retrievedItem;
}3. Use Filters for Complex Queries
// Good - flexible filtering
const request = store.get(item =>
item.priority > 5 &&
item.deadline < sim.now + 100 &&
item.status === 'ready'
);4. Monitor Store State
function* monitor() {
while (true) {
console.log(`Store: ${store.size}/${store.capacity} items`);
console.log(` Put queue: ${store.putQueueLength}`);
console.log(` Get queue: ${store.getQueueLength}`);
if (store.size === 0 && store.getQueueLength > 0) {
console.log('Warning: Stockout condition!');
}
yield* timeout(10);
}
}5. Name Stores for Statistics
// Always provide a name
const warehouse = new Store<Pallet>(sim, 100, {
name: 'Warehouse'
});
// After simulation
console.log(`Utilization: ${(warehouse.stats.averageSize / warehouse.capacity * 100).toFixed(1)}%`);Differences from Buffer
| Feature | Store | Buffer |
|---|---|---|
| Contents | Distinct objects | Homogeneous tokens (numbers) |
| Operations | put(item), get(filter?) | put(amount), get(amount) |
| Retrieval | FIFO or filtered | By quantity |
| Use Cases | Parts, vehicles, jobs | Fuel, money, materials |
| Item Tracking | Individual objects | Numeric level |
| Filter Support | Yes | No |
Performance Considerations
Filter Efficiency
// Efficient - simple property check
const request = store.get(item => item.id === targetId);
// Less efficient - complex computation
const request = store.get(item =>
expensiveCalculation(item) > threshold
);Large Stores
// For very large stores, consider:
// 1. Using simple filters
// 2. Monitoring getQueue length
// 3. Indexing items externally if needed
const largeStore = new Store<Item>(sim, 10000, {
name: 'Large Store'
});See Also
- Buffer API - Managing homogeneous quantities
- Resource API - Capacity-based resources
- Guide: Resources - Resource patterns and examples
- Statistics API - Understanding store statistics