-
Couldn't load subscription status.
- Fork 108
Description
Feature Request
Add a collection.awaitPersisted(id) method that returns a promise resolving when all active mutations for a given record have been persisted.
Motivation
Currently, to wait for a record to sync, you need to manually track the transaction:
const tx = collection.update(todoId, draft => { draft.completed = true })
await tx.isPersisted.promiseThis requires holding onto the transaction reference. For fire-and-forget mutations or when multiple components might update the same record, it would be cleaner to await persistence by record ID:
collection.update(todoId, draft => { draft.completed = true })
// Later, anywhere in the app...
await collection.awaitPersisted(todoId)Proposed API
collection.awaitPersisted(id: TKey): Promise<void>Behavior:
- Finds all active transactions affecting the given key
- Waits for them to complete via
Promise.all() - Rechecks for new mutations after completion (new txs might have started while waiting)
- Loops until no active mutations remain
- Resolves immediately if no active mutations exist for that key
Implementation
async awaitPersisted(key: TKey): Promise<void> {
while (true) {
const info = this.getOptimisticInfo(key)
if (!info?.isOptimistic) return
await Promise.all(info.mutations.map(m => m.transaction.isPersisted.promise))
// Recheck - new mutations might have arrived while waiting
}
}The loop is necessary because new mutations can be added while waiting for current ones to complete.
Use Cases
- Navigation after save: Wait for sync before navigating away
- Sequential operations: Ensure first update completes before starting dependent operation
- Loading states: Show spinner until all pending changes for a record are persisted
- Testing: Easier to wait for specific records to sync in tests
Example Usage
// Simple case
await collection.awaitPersisted(todoId)
// Navigation
async function handleSaveAndClose() {
collection.update(todoId, draft => { draft.text = newText })
await collection.awaitPersisted(todoId)
navigate('/todos')
}
// Testing
test('updates are persisted', async () => {
collection.update('todo-1', draft => { draft.completed = true })
await collection.awaitPersisted('todo-1')
expect(mockApi.updateTodo).toHaveBeenCalled()
})Implementation Notes
Builds on the existing getOptimisticInfo() infrastructure from #660 which already scans active transactions for a given key.