У меня есть сценарий Node.js, который подписывается на службу уведомлений и запускает множество вещей при получении push-уведомления. Однако служба иногда отправляет несколько уведомлений для одного и того же события, поэтому, чтобы избежать дублирования работы, я сделал базовый семафор для блокировки других задач.
Проблема в том, что Node все еще продолжает выполнение, несмотря на то, что Я вижу файл, созданный на диске. Я пробовал несколько разных решений, но я думаю, что проблема связана с отсутствием у меня опыта работы с моделью выполнения JS, есть кое-что, чего я не знаю о том, как это работает, что мешает моему решению работать. Как я могу это исправить?
const fse = require('fs-extra');
// notification handler callback
function handleRequest(data)
{
try{
var semaphore = fse.readJsonSync(__dirname + '/' + objectId);
console.info('task already running, stopping');
return;
}catch(err){
// semaphore doesn't exist, ok to proceed
console.info('starting new task');
fse.writeJson(__dirname + '/' + objectId, {objectId: objectId})
.then(stepOne).catch(rejectPromise)
.then(resp => stepTwo(resp, data)).catch(rejectPromise)
.then(resp => stepThree(resp, extra)).catch(rejectPromise)
.then(resp => stepFour(resp, argument)).catch(rejectPromise)
.then(sleep(20000))
.then(resp => releaseLock(objectId))
.catch(resp => rejectionHandler(resp);
}
}
function releaseLock(objectId)
{
return fse.remove(__dirname + '/' + objectId);
}
Другие вещи, которые я пробовал
@priyanshgupta да, это другой взгляд на это. Единственный способ попасть в блок catch - это если файл не существует, но я вижу, что он создан в файловой системе.
fse.writeJson является асинхронным, поэтому теоретически handleRequest можно было бы вызвать в другой раз до того, как fse.writeJson выполнит какую-либо запись. Вы должны создать монопольную блокировку для objectId, и только если блокировка была создана успешно, вы должны выполнить эту задачу.
Значит, вам нужно запретить кому-либо вызывать handle request, если вы видите в fs какой-либо файл с тем же objectId?
@ t.niese, значит, тот факт, что я привязал его через обещание к другим функциям, ничего не меняет? Идея в том, что stepOne не должен запускаться до создания файла.
@priyanshgupta да именно



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Нет необходимости создавать внешний файл для поддержки блокировок, вы можете сделать что-то вроде этого, это также даст вам повышение производительности (меньше операций ввода-вывода).
const fse = require('fs-extra');
// notification handler callback
class NamedLocks {
constructor() {
this._pid = {};
}
acquire(pid) {
if (this._pid[pid]) {
// process is locked
// handle it
return Promise.reject();
}
this._pid[pid] = true;
return Promise.resolve();
}
release(pid) {
delete this._pid[pid];
}
}
const userLocks = new NamedLocks();
function handleRequest(data) {
userLocks.acquire(objectId)
.then(() => {
// semaphore doesn't exist, ok to proceed
console.info('starting new task');
fse.writeJson(__dirname + '/' + objectId, { objectId: objectId })
.then(stepOne).catch(rejectPromise)
.then(resp => stepTwo(resp, data)).catch(rejectPromise)
.then(resp => stepThree(resp, extra)).catch(rejectPromise)
.then(resp => stepFour(resp, argument)).catch(rejectPromise)
.then(sleep(20000))
.then(resp => userLocks.release(objectId))
.catch(resp => rejectionHandler(resp))
}).catch(() => {
// handle lock exist condition here
});
};
В этом случае вы в основном запрашиваете блокировку, и если блокировка существует, обработайте ее в обработчике catch, иначе сделайте свое дело и освободите блокировку
Да ! великолепно, спасибо вам большое. Я только что проверил это, он отлично работает
Я бы предпочел использовать release(pid) { delete this._pid[pid]; }, иначе у вас будет небольшая утечка памяти.
В качестве примечания: это будет работать нормально, если вы не запустите свое приложение как кластер с несколькими рабочими процессами. Поэтому, если вы планируете использовать больше, чем на ядре процессора в будущем, вы можете добавить проверку в NamedLocks, которая в этом случае вызовет выполнение, которое напоминает вам, что вам нужно реализовать файловую или любую другую глобальную блокировку. @blackbird
То есть вы говорите, что даже если в блоке
tryнет ошибки, код попадает в блокcatch?