首页 > 解决方案 > 在 Node 中运行后台进程的最佳实践?

问题描述

比如说我有以下代码,一个简单的 UI 测试。

async function testMyCoolUI() {
  await uiFramework.openMyApp(url);
      
  await sleep(2000);
    
  await uiFramework.clickButtonX();

  await uiFramework.clickButtonY();
}

现在添加了一个新要求。在测试期间的任何时候,屏幕上都会出现一个弹出窗口,上面写着“你是机器人吗?”,我们必须选择“否”。

您将如何构建您的测试,以使这个“过程”可以在测试的背景中持续运行,并注意这个弹出窗口?我最初的想法是启动一个异步函数轮询弹出窗口,但不要等待testMyCoolUI.

async function testMyCoolUI() {
  await uiFramework.openMyApp(url);
      
  await sleep(2000);

  startPollingForPopup(); // this is an async function, but not waiting on it
    
  await uiFramework.clickButtonX();

  await uiFramework.clickButtonY();
}

然而,这感觉是错误的,并且承诺将无法解决,并且该过程不会很好地清理。在 JS 中“正确”执行此操作的方法是什么?

其他想法:

Promise.all([testMyCoolUI, pollForPopup]);

但在这种情况下,测试仍将在轮询解决之前完成。出于同样的原因,这里并Promise.race没有真正起作用。

标签: javascriptnode.jsasynchronousasync-await

解决方案


一个可以确保正确清理的良好代码结构模式是promise disposer 模式

async function testMyCoolUI() {
  await uiFramework.openMyApp(url); 
  await sleep(2000);
  await withPollingForPopup(async () => {
    await uiFramework.clickButtonX();
    await uiFramework.clickButtonY();
  });
}

async function withPollingForPopup(run) {
  try {
    startPollingForPopup(); // not waiting for anything
    return await run();
  } finally {
    stopPollingForPopup(); // optionally `await` it
  }
}

这假设有一个可以启动和停止的后台进程,可能是一个事件订阅。

或者,如果后台进程确实返回了一个拒绝错误的承诺并且您想尽快中止,您可以使用

async function withPollingForPopup(run) {
  const poll = runPollingForPopup();
  const [res] = await Promise.all([
    run().finally(poll.stop),
    poll.promise
  ]);
  return res;
}

推荐阅读