如何在函数中编写通用回滚或暂停

时间:2019-02-27 16:23:09

标签: javascript node.js promise async-await cancellation

这些示例将超级简化,但希望您能理解。

说我有一个函数,它可以执行一系列任务,并且可能需要很长时间:

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())?还是只有一种简单的方法可以做这种事情? TIA。

3 个答案:

答案 0 :(得分:0)

抛出错误实际上听起来不错,这可以做得很优雅:

 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();

答案 1 :(得分:0)

否,找不到您想要的东西。没有隐式包装某些内容的“任务”抽象,并且您不能中断任意函数。他们需要明确选择取消资格(例如,通过检查传递给他们的{"name":"customer-service","profiles":["master"],"label":null,"version":null,"state":null,"propertySources":[]} ,就像乔纳斯的回答一样)。您可以使用可以杀死的子进程来执行此操作,但这可能会留下不一致的状态。

答案 2 :(得分:0)

让我们分解一下:

承诺取消 存在。您需要一个polyfill来添加对Where Should the Code Live?的支持,该支持在被调用时将取消Promise执行。您可以阅读有关此here的更多信息。一旦安装了polyfill,您的诺言将有一个.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() }); 的最开始拍摄状态的快照,然后如果调用doSomeStuff,则更新状态以匹配开始的状态。但是,“回滚”并不是可以得到本机支持的预定义事物,这与数据库的回滚定义非常好不同。