【问题标题】:Break for event loop processing without breaking loopBreak 用于事件循环处理而不中断循环
【发布时间】:2019-09-03 22:01:22
【问题描述】:

我发现自己最近试图处理大量数据,我不得不做一些(我们称之为)创造性编码来获得所需的结果。

我知道处理将是一个多天的任务,所以我以一种可以中断和恢复的方式编写它。但让我难过的是找到一种处理 SIGINT/SIGTERM 样式事件的好方法,而不想破坏我的循环。

顺便说一句这些显然不是我的函数的真实名称

let closed = false;
function* syncGenerator() {
  yield* ~20-trillion-calculated-results;
}
function main() {
  try {
    for ( const value of syncGenerator() ) {
      syncWork(value);
      if (closed) {
        syncGracefulCleanup();
        break;
      }
    }
  } catch ( err ) {
    handleError(err);
  } finally {
    syncGracefulCleanup2();
  }
}
process.on('SIGINT', () => closed = true);

我没有意识到当我还在 for 循环中时,SIGINT 永远不会被处理,因此只会在整个数据集被处理后才能完成(一个非常愚蠢的疏忽,我想我误以为它会工作与 Arduino 硬件中断相同。D'oh。经验教训。

现在我意识到了这一点,我的解决方案是使用微任务异步等待技巧将我的一半代码放在滴答声的末尾。

let closed = false;
function* syncGenerator() {
  yield* ~20-trillion-calculated-results;
}
async function main() {
  try {
    for ( const value of syncGenerator() ) {
      syncWork(value);
      await new Promise(r => setImmediate(r)); // the new nasty
      if (closed) {
        syncGracefulCleanup();
        break;
      }
    }
  } catch ( err ) {
    handleError(err);
  } finally {
    syncGracefulCleanup2();
  }
}
process.on('SIGINT', () => closed = true);

现在,这可以按我的预期工作,并且 await 允许暂停循环并允许处理 SIGINT/SIGTERM,然后在 close 设置为 true 时继续。耶.. 但是该死的,它看起来很讨厌。 我希望有人可能有比这更好看的解决方案?

【问题讨论】:

  • 您的syncGenerator 可以改为异步生成器吗?
  • @noseratio 仅通过在该函数中移动 await setImmediate 代码。同样的问题,不同的位置。
  • 我的意思是,当“~20-trillion-calculated-results”可用时,您真的能以自然的方式异步生成它们吗?并使用for await loop消费?
  • @noseratio 我刚刚为仅在生成器和 for-await-of 前面编写 async 的场景编写了一个快速测试。但可悲的是,它按我想象的方式工作。如果没有在异步生成器中放置实际的异步任务,它的工作方式与同步生成器完全一样,所以我必须将await new Promise((r) => setImmidate(r)); 移到那里。正如我所说,同样的问题,只是位置不同。

标签: javascript node.js event-loop


【解决方案1】:

你需要保留标志,但你可以让你的主要逻辑更简单。考虑

let closed = false;
function* syncGenerator() {
  yield* ~20-trillion-calculated-results;
}

function main( generator = syncGenerator() ) 
{
    if (closed) 
    {
      syncGracefulCleanup();
    }
    else 
    {
        // assuming syncWork returns false when 
        // there are no more value
        if ( syncWork( generator.next() ) )
        {
            // next-ish off to the event loop. this is NOT recursive, 
            // even though it may look a bit like it
            setTimeout( () => main(generator), 0 );         
        }
    }
}

process.on('SIGINT', () => close = true);

【讨论】:

  • 我不相信生成器是这样工作的。这会给我每次生成器的第一个结果。然后我需要存储第二个全局变量来保存迭代器并调用 it.next();我不认为这“更好”
  • 哦,你怀疑 Thomasses :-) 你当然是对的。但也不是,因为我的更新仍然很漂亮,恕我直言。
  • 而且没有额外的全局^^
  • 您似乎也忘记了您需要测试 generator.next().done,否则当生成器用完值时,主代码将永远循环。在这一点上,代码开始看起来比原来的更意大利面条,并且没有考虑到我上面的代码是一个非常非常精简的版本,我什至没有提到包装整个 for 循环的 try/catch/finally
  • 对,我对生成器不太熟悉。你可以让 syncWork 返回 true / false 以指示生成器何时完成?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-05
  • 1970-01-01
  • 2014-01-14
  • 2012-03-05
  • 1970-01-01
  • 2019-08-06
相关资源
最近更新 更多