Skip to content

Creates a "Gate" object with multiple locks that can be locked and unlocked. Useful for controlling async events

License

Notifications You must be signed in to change notification settings

jonwyett/jw-gate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jw-gate

A reactive coordination library that manages multiple conditions through an event-driven "Gate" with independent "locks". Perfect for two key scenarios:

  1. UI State Management - Track multiple async operations and show progress as each completes
  2. 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');

// Track multiple file uploads
const uploadGate = new Gate(['file1', 'file2', 'file3'], true);

// Update UI when all files are complete
uploadGate.on('unlocked', () => {
  updateUI('All uploads complete! 🎉');
});

// Start uploads and update progress individually
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

// Monitor changing conditions
const systemGate = new Gate(['network', 'power', 'sensors'], true);

// React immediately when all conditions are met
systemGate.on('unlocked', () => {
  console.log('System ready - starting operation');
  startOperation();
});

systemGate.on('locked', () => {
  console.log('Conditions changed - pausing operation');
  pauseOperation();
});

// Conditions change independently over time
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);

// Show progress as each step completes
uploadGate.on('unlocked', () => {
  showMessage('Upload complete! File is ready to use.');
  enableDownloadButton();
});

// Validate file
validateFile(file).then(valid => {
  if (valid) {
    showProgress('Validation complete ✓');
    uploadGate.setLock('validation', false);
  }
});

// Upload to server  
uploadToServer(file).then(() => {
  showProgress('Upload complete ✓');
  uploadGate.setLock('upload', false);
});

// Server processing
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();
});

// Sensors update conditions independently
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();
});

// Resource monitors update conditions
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)    // Register event listener
gate.off(event, callback)   // Remove event listener

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)              // Set lock state (true = locked)
gate.setLockWithTimeout(lock, state, ms)   // Set lock with automatic timeout
gate.resetAll(state)                       // Set all locks to same state

State Inspection

gate.getState()        // Get complete state object
gate.isUnlocked()      // Check if gate is currently unlocked
gate.getLockedCount()  // Count of currently locked locks
gate.getTotalLocks()   // Total number of locks

State Object

The getState() method returns:

{
  state: 'locked' | 'unlocked',
  locks: {
    [lockName: string]: boolean  // true = locked, false = unlocked
  },
  isLocked: boolean
}

Error Handling

All errors are emitted as events rather than thrown, preventing crashes:

gate.on('error', (errorMessage) => {
  console.error('Gate error:', errorMessage);
  // Handle error appropriately
});

Browser Support

Works in both Node.js and browser environments:

<script src="jw-gate.js"></script>
<script>
  const gate = new Gate(['condition1', 'condition2']);
  // Use normally
</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

About

Creates a "Gate" object with multiple locks that can be locked and unlocked. Useful for controlling async events

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors