jw-gate
A reactive coordination library that manages multiple conditions through an event-driven "Gate" with independent "locks". Perfect for two key scenarios:
- UI State Management - Track multiple async operations and show progress as each completes
- Dynamic Condition Monitoring - React to conditions that change over time (IoT, sensors, resources)
Unlike promise-based coordination, jw-gate lets you respond immediately to individual condition changes rather than waiting for everything to complete at once.
Why jw-gate?
For UI Progress Tracking: Promise.all()
only tells you when everything is done - you can't show individual completions. jw-gate lets you update your UI as each operation finishes.
For Dynamic Conditions: Traditional promises are one-shot, but real-world conditions change over time. jw-gate provides reactive coordination that responds immediately when conditions shift.
Features
- 🎯 Track multiple async operations with individual progress updates
- 🚪 Reactive coordination of changing conditions over time
- 🔄 Event-driven architecture - respond immediately to state changes
- ⏱️ Automatic timeout support for temporary conditions
- 🔍 Comprehensive state monitoring and introspection
- 🛡️ Error handling through events (no crashes)
- 🌐 Works in both Node.js and browser environments
- 📦 Zero dependencies
Installation
npm install jw-gate
Quick Start
UI Progress Tracking
const Gate = require('jw-gate');
const uploadGate = new Gate(['file1', 'file2', 'file3'], true);
uploadGate.on('unlocked', () => {
updateUI('All uploads complete! 🎉');
});
uploadFile1().then(() => {
updateUI('File 1 complete ✓');
uploadGate.setLock('file1', false);
});
uploadFile2().then(() => {
updateUI('File 2 complete ✓');
uploadGate.setLock('file2', false);
});
uploadFile3().then(() => {
updateUI('File 3 complete ✓');
uploadGate.setLock('file3', false);
});
Dynamic Condition Monitoring
const systemGate = new Gate(['network', 'power', 'sensors'], true);
systemGate.on('unlocked', () => {
console.log('System ready - starting operation');
startOperation();
});
systemGate.on('locked', () => {
console.log('Conditions changed - pausing operation');
pauseOperation();
});
networkMonitor.on('connected', () => systemGate.setLock('network', false));
networkMonitor.on('disconnected', () => systemGate.setLock('network', true));
powerMonitor.on('stable', () => systemGate.setLock('power', false));
powerMonitor.on('unstable', () => systemGate.setLock('power', true));
Common Examples
UI Progress Tracking
File Upload Dashboard
const uploadGate = new Gate(['validation', 'upload', 'processing'], true);
uploadGate.on('unlocked', () => {
showMessage('Upload complete! File is ready to use.');
enableDownloadButton();
});
validateFile(file).then(valid => {
if (valid) {
showProgress('Validation complete ✓');
uploadGate.setLock('validation', false);
}
});
uploadToServer(file).then(() => {
showProgress('Upload complete ✓');
uploadGate.setLock('upload', false);
});
processOnServer(file).then(() => {
showProgress('Processing complete ✓');
uploadGate.setLock('processing', false);
});
Multi-Step Form Validation
const formGate = new Gate(['email', 'password', 'terms'], true);
formGate.on('unlocked', () => {
enableSubmitButton();
showMessage('Form ready for submission');
});
formGate.on('locked', () => {
disableSubmitButton();
});
emailField.on('validated', () => {
showCheckmark('email');
formGate.setLock('email', false);
});
passwordField.on('validated', () => {
showCheckmark('password');
formGate.setLock('password', false);
});
termsCheckbox.on('checked', () => {
showCheckmark('terms');
formGate.setLock('terms', false);
});
Dynamic Condition Monitoring
Smart Device Control
const deviceGate = new Gate(['safety', 'network', 'power'], true);
deviceGate.on('unlocked', () => {
console.log('Device activated - all systems go');
device.start();
statusLight.setGreen();
});
deviceGate.on('locked', () => {
console.log('Safety conditions changed - device stopped');
device.emergencyStop();
statusLight.setRed();
});
safetySystem.on('safe', () => deviceGate.setLock('safety', false));
safetySystem.on('unsafe', () => deviceGate.setLock('safety', true));
networkMonitor.on('connected', () => deviceGate.setLock('network', false));
networkMonitor.on('disconnected', () => deviceGate.setLock('network', true));
powerMonitor.on('stable', () => deviceGate.setLock('power', false));
powerMonitor.on('fluctuation', () => deviceGate.setLock('power', true));
Resource-Aware Processing
const processingGate = new Gate(['cpu', 'memory', 'disk'], true);
processingGate.on('unlocked', () => {
console.log('Resources available - starting batch job');
startBatchProcessing();
});
processingGate.on('locked', () => {
console.log('Resource constraints - pausing batch job');
pauseBatchProcessing();
});
cpuMonitor.on('available', () => processingGate.setLock('cpu', false));
cpuMonitor.on('busy', () => processingGate.setLock('cpu', true));
memoryMonitor.on('sufficient', () => processingGate.setLock('memory', false));
memoryMonitor.on('low', () => processingGate.setLock('memory', true));
diskMonitor.on('space', () => processingGate.setLock('disk', false));
diskMonitor.on('full', () => processingGate.setLock('disk', true));
API Reference
Constructor
new Gate(lockNames, initialState = false)
lockNames
: Array of strings representing lock names
initialState
: Boolean indicating initial state of locks (default: false = unlocked)
Event Management
gate.on(event, callback)
gate.off(event, callback)
Events:
'locked'
: Emitted when gate becomes locked (one or more locks engaged)
'unlocked'
: Emitted when gate becomes unlocked (all locks disengaged)
'error'
: Emitted when an error occurs
Lock Management
gate.setLock(lockName, state)
gate.setLockWithTimeout(lock, state, ms)
gate.resetAll(state)
State Inspection
gate.getState()
gate.isUnlocked()
gate.getLockedCount()
gate.getTotalLocks()
State Object
The getState()
method returns:
{
state: 'locked' | 'unlocked',
locks: {
[lockName: string]: boolean
},
isLocked: boolean
}
Error Handling
All errors are emitted as events rather than thrown, preventing crashes:
gate.on('error', (errorMessage) => {
console.error('Gate error:', errorMessage);
});
Browser Support
Works in both Node.js and browser environments:
<script src="jw-gate.js"></script>
<script>
const gate = new Gate(['condition1', 'condition2']);
</script>
When NOT to Use jw-gate
- Simple one-time coordination: Use
Promise.all()
if you don't need individual progress updates
- Single async operation: Use
async/await
instead
- Static boolean logic: Use regular conditionals instead
jw-gate excels when you need immediate feedback on individual condition changes or reactive coordination of changing conditions.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT