【问题标题】:Waiting in a while loop on an async function (Node.js/ES6)等待异步函数的 while 循环 (Node.js/ES6)
【发布时间】:2020-03-27 07:29:17
【问题描述】:

我正在编写一个 Windows Node.js 服务器应用程序(使用 ES6 顺便说一句)。

我想做的第一件事 - 在顶级代码中 - 是坐在一个 while 循环中,调用一个搜索特定注册表键/值的异步函数。此函数已被“证明” - 如果找到,则返回值数据,否则抛出:

async GetRegValue(): Promise<string> { ... }

我需要坐在一个while循环中,直到注册表项存在,然后获取值数据。 (重试之间有延迟)。 我想我知道如何等待异步调用完成(一种或另一种方式),然后再进行其余的启动,但我不知道如何坐在循环中等待它 成功

请就如何实现这一目标提供任何建议? (我对 typescript 还很陌生,仍然难以理解所有异步/等待场景!)

谢谢

编辑

谢谢各位。我知道我对我的代码“含糊不清”——我不想尝试我的真实/伪代码,因为它们可能都忽略了你希望能帮助我理解的要点。 所以我只是将其保留为文字描述......不过我会尝试:

async GetRegValue(): Promise<string> {

    const val: RegistryItem = await this.GetKeyValue(this.KEY_SW, this.VAL_CONN);
    return val.value
}

private async GetKeyValue(key: string, name: string): Promise<RegistryItem> {

    return await new Promise((resolve, reject) => {
        new this.Registry({
            hive: this.Hive, key
        }).get(name, (err, items) => {

            if (err) {
                reject(new Error('Registry get failed'));
            }
            else {
                resolve( items );
            }
        });
    })
        .catch(err => { throw err });
}

所以我想做这样的事情:

let keyObtained = false
let val
while (keyObtained == false)
{
    // Call GetRegValue until val returned, in which case break from loop
    // If exception then pause (e.g. ~100ms), then loop again
    }
}

// Don't execute here till while loop has exited

// Then use 'val' for the subsequent statements

正如我所说,GetRegValue() 在我使用它的其他地方工作正常,但在这里我试图暂停进一步执行(并重试),直到它返回一个值

【问题讨论】:

  • 使用awaitthen
  • 你能提供一个更好的例子来说明你在做什么吗?
  • 请提供您尝试执行while 循环的实际代码。您不能在 Javascript 中执行 while 循环旋转以等待某事发生,因为这会占用 JS 的单线程,而您正在等待的事情永远无法自行运行。因此,我们需要查看实际代码并了解您实际在等待什么,以便最好地建议如何编写代码。真实代码,真实数据,敬请期待。包括尽可能多的细节。
  • Javascript 是一种事件驱动的语言。这意味着了解某事何时发生的有效方法是为您正在等待的事物触发的事件注册一个事件侦听器。你不投票,你不while()循环它,等等......如何为你想要的改变设置一个事件监听器完全取决于你没有透露它是什么,所以我们无法回答你的问题照原样。
  • 这里最好的设计是更新注册表以公开您可以注册感兴趣的事件或回调的代码。你不想成为polling node.js 中的一个值(非常低效)。您没有显示这个 Registry 对象是什么。也许它有自己的活动可以注册。如果没有,那么您应该集中所有修改注册表的代码并创建某种通知,其他代码可以在某个值更改时注册。这将创建一个事件驱动的解决方案,这是在 node.js 中编程的适当方式。

标签: node.js typescript async-await


【解决方案1】:

您可能只使用递归。下面是一个示例,说明如何继续调用 GetRegValue 函数,直到使用下面的 retryReg 函数解析。

如果遇到 catch 案例,它将一遍又一遍地调用GetRegValue,直到成功解决。

你应该在 catch() 中添加一个计数器,如果你尝试了 x 次你就放弃了。

请记住,我模拟了整个 GetRegValue 函数,但鉴于你所说的,这仍然对你有用。

let test = 0;

function GetRegValue() {
  return new Promise((resolve, reject) => {
    setTimeout(function() {
      test++;

      if (test === 4) {
        return resolve({
          reg: "reg value"
        });
      }

      reject({
        msg: "not ready"
      });

    }, 1000);
  });
}

function retryReg() {
  GetRegValue()
    .then(registryObj => {
      console.log(`got registry obj: ${JSON.stringify(registryObj)}`)
    })
    .catch(fail => {
      console.log(`registry object is not ready: ${JSON.stringify(fail)}`);
      retryReg();
    });
}

retryReg();

【讨论】:

  • 由于 OP 根本没有包含有关他们实际在做什么的任何细节,这是一个疯狂的猜测,可能合适也可能不合适。我们不知道是不是。
  • 哦,我正在用 Typescript 编码,顺便说一句
  • @ColH 我试图向您传达的相同概念对于打字稿仍然有效
【解决方案2】:

我不明白你为什么需要这条线:

.catch(err => { throw err });

while 的循环条件在这种情况下用处不大,因为您实际上不需要状态变量或表达式来确定循环是否应该继续:

let val;
while (true)
{
    try {
       val = await GetRegValue(/* args */);
       break;

    } catch (x) {
        console.log(x); // or something better
    }

    await delay(100);
}

如果对val 的赋值成功,我们将它传递给break; 语句,因此我们成功退出循环。否则我们跳转到catch 块并记录错误,等待 100 毫秒再试一次。

使用for 循环可能会更好,因此对重试次数设置合理的限制。

请注意,delaynpm package of the same name 中可用。大致相同:

await new Promise(res => setTimeout(res, 100));

【讨论】:

  • 虽然这可能有效,但 node.js 中的“轮询”循环从来都不是最好的设计,因为它会浪费各种 CPU,只是一遍又一遍地检查一个变量。更好地找到更改数据的源并创建一个与 node.js 的整体架构相匹配的事件驱动系统,并且效率更高。
  • @jfriend00 确定我们是否是提供数据的系统的作者。但经常在分布式系统中,我们必须通过将我们无法控制的不可靠服务捆绑在一起来模拟可靠性。在这种情况下,重试是必不可少的工具。
  • 我只是向 OP 指出,这不是解决此问题的最佳方法。从 OP 代码的 sn-ps 看来,它们控制了 GetRegValue() 的代码,因此也可以控制设置值的代码。如果他们这样做,他们可以从EventEmitter 派生或添加EventEmitter 作为实例数据并创建一个通知系统,这是解决此类问题的“正确”方式。我永远不会在应该扩展的生产代码中放置这样的循环。想象一下,对于系统上的每个活跃用户,您都有一个或多个这样的循环。
  • 如果 OP 显示更多相关代码以让我们看到足够多的内容以向他们展示更好的方式,我会通过通知系统提供我自己的答案,但他们没有显示足够的代码。
  • 谢谢大家。才刚刚开始工作,所以还没有完全吸收所有的建议。但是要添加一些我没有说清楚的信息:该应用程序在启动时将只有其中一个循环 - 注册表值告诉它从哪里获取其运行时配置,并且(例如,安装后)它将坐在这里等待在它能够进一步启动之前设置(由不同的应用程序)。 GetKeyValue() 用于获取注册表值,我不控制任何较低的代码(它使用'node-winreg'包)
猜你喜欢
  • 2018-09-25
  • 2019-07-02
  • 2018-02-05
  • 2021-10-18
  • 2017-09-14
  • 1970-01-01
  • 2020-08-01
  • 2020-04-20
  • 2019-06-02
相关资源
最近更新 更多