Как написать общий откат или останов в функции - PullRequest
0 голосов
/ 27 февраля 2019

Эти примеры будут очень упрощенными, но, надеюсь, вы поймете суть.

Скажем, у меня есть функция как таковая, которая выполняет ряд задач и может занять много времени:

async function doSomeStuff() {
  await aLongTask();
  await anotherBigOldTask();
  await bigNestedTaskThatTakesForever();
  return Promise.resolve('Done');
}

Теперь у меня есть часы chokidar, которые ищут изменения файлов и запускают указанную функцию:

const watcher = chokidar.watch(watchURLs, {
  ignored: /\.git/,
  ignoreInitial: true,
  persistent: true,
  ignorePermissionErrors: true,
  atomic: 500,
});
watcher.on('all', async (event, filePath) => {
  await doSomeStuff();
});

Проблема заключается в том, что, когда файлы меняются много раз во время работы doSomeStuff(), он пытаетсязапустить сборку несколько раз.Это приводит к целому ряду ошибок, которые я грубо исправил с помощью тайм-аута:

if (!locked) {
  await doSomeStuff().finally(() => {
    locked = false;
    info('Waiting for file changes in', ...watchURLs);
  });
}
else {
  debug('Attempting to trigger a locked build');
}
locked = true;
clearTimeout(lockTimeout);
lockTimeout = setTimeout(() => {
  locked = false;
  debug('Unlocked due to timeout');
}, 10000);

Это останавливает вещи от полного взрыва, но это означает, что все всегда будет отсутствоватьдаты.Только при сохранении файла после истечения периода отсрочки блокировки он будет извлечен.И если загрузка файлов сохранена, она может быть собрана только с половиной обновленных включенных.

Итак, как можно полностью отменить / остановить / откатить функцию doSomeStuff() непосредственно перед новой сборкойсрабатывает?Я не хочу делать что-то, где я устанавливаю переменную, которая заставляет doSomeStuff() возвращать то, что я обертываю вокруг каждой задачи, потому что это не является немедленным и не работает для любых сложных функций, вложенных внутрь.Это почти как если бы я хотел выдать ошибку в функцию, а не эту функцию, управляющую тем, что бросить для себя.

Может кто-нибудь придумать способ немедленного принудительного завершения функциивыполнение без уничтожения всего сценария (а-ля process.exit())?Или если есть простой способ сделать это?ТИА.

Ответы [ 3 ]

0 голосов
/ 27 февраля 2019

Нет, то, что вы ищете, невозможно.Не существует абстракции «задача», которая явно скрывает что-либо, и вы не можете прерывать произвольные функции.Они должны явно отказаться от отмены (например, проверяя breakpoint, который им передается, как в ответе Джонаса).Вы можете сделать это, используя дочерний процесс, который вы можете уничтожить, но это может оставить несовместимое состояние.

0 голосов
/ 27 февраля 2019

Давайте разберемся:

Отмена обещания Это существует .Для добавления поддержки .cancel вам понадобится полифилл, который при вызове отменит выполнение обещанияВы можете прочитать больше об этом здесь .Как только вы установите полифилл, ваше обещание будет иметь .cancel, поэтому вы можете иметь:

const doSomeStuff = () => {
    return new Promise(resolve => {
        ...
    })
}

let prmise = null;
const watcher = chokidar.watch(watchURLs, {
  ignored: /\.git/,
  ignoreInitial: true,
  persistent: true,
  ignorePermissionErrors: true,
  atomic: 500,
});
watcher.on('all', (event, filePath) => {
  promise = doSomeStuff();
});
watcher.on('change', () => {
  promise && promise.cancel()
});

Rollbar Этот не существует .Вы должны реализовать это самостоятельно.Возможно, сделайте снимок состояния в самом начале doSomeStuff, а затем, если был вызван .cancel, обновите состояние, чтобы оно соответствовало начальному состоянию.Но «откат» - это не предопределенная вещь, которая может иметь встроенную поддержку, в отличие от баз данных, где откат имеет довольно хорошее определение.

0 голосов
/ 27 февраля 2019

Внесение ошибки на самом деле звучит хорошо, это можно сделать довольно элегантно:

 const cancellable = fn => (...args) => {
  let cancel = false;
  const breakpoint = () => {
    if(cancel) throw new Error("Cancelation");
  };

  return {
    result: fn(breakpoint, ...args).catch(console.log),
    stop() { cancel = true; },
  };
};

Это можно использовать как:

const doSomeStuff = cancellable(async function doSomeStuff(breakpoint) {
  await aLongTask();
  breakpoint();
  await anotherBigOldTask();
  breakpoint();
  await bigNestedTaskThatTakesForever();
  return "Done";
 });

 let task = doSomeStuff();
 task.stop();
...