Ответ
В Node.js контексте управление состоянием имеет свою специфику. Вот подходы, которые я использовал:
1. EventEmitter для реактивного состояния:
const EventEmitter = require('events');
class ApplicationState extends EventEmitter {
constructor() {
super();
this._state = {
connectedUsers: 0,
serverStatus: 'running',
lastError: null
};
}
setState(updates) {
this._state = { ...this._state, ...updates };
this.emit('stateChanged', this._state);
}
getState() {
return { ...this._state };
}
}
// Использование
const appState = new ApplicationState();
appState.on('stateChanged', (newState) => {
console.log('State updated:', newState);
// Обновляем метрики, логируем и т.д.
});
appState.setState({ connectedUsers: 42 });
2. Finite State Machines (XState) для сложной логики:
const { Machine, interpret } = require('xstate');
const paymentMachine = Machine({
id: 'payment',
initial: 'idle',
states: {
idle: {
on: { START: 'processing' }
},
processing: {
on: {
SUCCESS: 'completed',
FAILURE: 'failed'
}
},
completed: { type: 'final' },
failed: {
on: { RETRY: 'processing' }
}
}
});
const paymentService = interpret(paymentMachine)
.onTransition((state) => {
console.log('Payment state:', state.value);
})
.start();
paymentService.send('START');
3. Shared memory для worker процессов: При использовании кластеризации или worker_threads можно использовать:
- SharedArrayBuffer для примитивных данных
- Redis как распределенное хранилище состояния
- Базы данных (PostgreSQL, MongoDB) для персистентного состояния
4. Контекст выполнения (Execution Context): Для передачи состояния через middleware цепочку в Express:
// Установка контекста
app.use((req, res, next) => {
req.context = {
requestId: uuid(),
userId: req.user?.id,
startTime: Date.now()
};
next();
});
// Использование в обработчике
app.get('/api/data', async (req, res) => {
const data = await db.query('SELECT * FROM data', {
requestId: req.context.requestId
});
res.json(data);
});
Выбор подхода зависит от требований: для простых случаев достаточно EventEmitter, для сложных бизнес-процессов — конечные автоматы, для распределенных систем — Redis.
Ответ 18+ 🔞
Ну вот смотри, как это в Node.js обычно бывает. Ты пишешь приложение, а потом понимаешь, что состояние размазано по всему коду, как говно по стенке после взрыва унитаза. И начинаешь думать: "Ёпта, а как это всё организовать, чтобы не было пиздеца?"
1. EventEmitter — классика, как борщ с пампушками. Просто и понятно, особенно если ты не хочешь овердохуища сложностей.
const EventEmitter = require('events');
class ApplicationState extends EventEmitter {
constructor() {
super();
this._state = {
connectedUsers: 0,
serverStatus: 'running',
lastError: null
};
}
setState(updates) {
this._state = { ...this._state, ...updates };
this.emit('stateChanged', this._state);
}
getState() {
return { ...this._state };
}
}
// Использование
const appState = new ApplicationState();
appState.on('stateChanged', (newState) => {
console.log('State updated:', newState);
// Обновляем метрики, логируем и т.д.
});
appState.setState({ connectedUsers: 42 });
Вот смотри, ты создал объект, и он сам всем кричит: "Эй, пацаны, у меня состояние поменялось!" Все подписчики получают уведомление и делают свои дела. Удобно, но если логика станет сложной, это превратится в ёперный театр. Подозрение ебать чувствую, что начнётся спагетти из событий.
2. Конечные автоматы (XState) — для тех, кто любит порядок, как в армии. Когда твоя бизнес-логика напоминает полосу препятствий для десантника, тут без машины состояний — нихуя.
const { Machine, interpret } = require('xstate');
const paymentMachine = Machine({
id: 'payment',
initial: 'idle',
states: {
idle: {
on: { START: 'processing' }
},
processing: {
on: {
SUCCESS: 'completed',
FAILURE: 'failed'
}
},
completed: { type: 'final' },
failed: {
on: { RETRY: 'processing' }
}
}
});
const paymentService = interpret(paymentMachine)
.onTransition((state) => {
console.log('Payment state:', state.value);
})
.start();
paymentService.send('START');
Смотри, какая красота: состояние платежа не может просто так взять и перепрыгнуть из idle в completed. Ему нужно пройти через processing. Это как в жизни: чтобы получить результат, надо поработать, а не просто сидеть и бздеть. Если что-то пошло не так — есть чёткий путь для RETRY. Волнение ебать пропадает, потому что ты видишь всю схему как на ладони.
3. Общая память для воркеров — когда нужно, чтобы все пели с одного листа. Запустил кластер, а состояния между процессами как хуй с горы — каждый сам по себе. Терпения ноль ебать, когда данные не синхронизируются.
Тут варианты:
- SharedArrayBuffer — для цифр, флагов. Быстро, но опасно, как ходить по охуенно тонкому льду. Одно неверное движение — и segmentation fault.
- Redis — классика для распределённого состояния. Все воркеры подключаются к одному редису и читают/пишут туда. Надёжно, но это ещё одна зависимость в проекте.
- База данных (PostgreSQL, MongoDB) — если состояние должно пережить перезапуск сервера. Медленнее, но зато персистентно.
4. Контекст выполнения — чтобы не таскать данные через всю цепочку вызовов, как сумку с картошкой. Особенно актуально в Express.js, где каждый middleware может что-то добавить к запросу.
// Установка контекста
app.use((req, res, next) => {
req.context = {
requestId: uuid(),
userId: req.user?.id,
startTime: Date.now()
};
next();
});
// Использование в обработчике
app.get('/api/data', async (req, res) => {
const data = await db.query('SELECT * FROM data', {
requestId: req.context.requestId
});
res.json(data);
});
Вот ты положил в req.context всё, что нужно для этого запроса: ID, время начала, пользователя. И дальше любой middleware или обработчик может это достать. Удобно, логично, и не нужно передавать кучу параметров в каждую функцию. Сам от себя охуеешь, насколько код становится чище.
Итог, чувак:
- Для простого UI-like состояния — EventEmitter, ебушки-воробушки.
- Для сложной бизнес-логики с кучей условий — XState, чтобы не сойти с ума.
- Для кластера — Redis или база, иначе будет бардак.
- Для передачи данных по цепочке middleware — контекст запроса, и не еби себе мозг.
Выбирай по ситуации, и не делай из мухи слона. Иногда проще — значит лучше.