【问题标题】:Promise - Resolving after multiple eventsPromise - 在多个事件后解决
【发布时间】:2018-08-29 08:56:02
【问题描述】:

我正在使用 Promise 异步连接到 API。 API 发出两个事件:connectedsocketConnected

根据我的connect() 函数,我想等待这两个事件触发。

connect() {
  return new Promise((resolve,reject)=>{
    this._client.on('connected', resolve);
    this._client.on('socketConnected', resolve);
    setTimeout(reject, 5000);
  }
}

但是,您只能解决一次,我希望这两个事件在 promise 解决之前触发。我该如何设置它以使其行为如此?

【问题讨论】:

    标签: javascript node.js promise


    【解决方案1】:

    您需要使用Promise.all 创建两个 承诺,每个事件一个,当两者都解决时 - 当Promise.all 解决时 - 您可以解决connect 返回的承诺:

    connect() {
      return new Promise((resolveAll, rejectAll) => {
        Promise.all([
          new Promise(res1 => this._client.on('connected', res1)),
          new Promise(res2 => this._client.on('socketConnected', res2))
        ]).then(resolveAll);
        setTimeout(rejectAll, 5000);
      });
    }
    

    如果您希望尽快进行垃圾回收,您还可以在 Promise.all 解析时清除超时:

    connect() {
      return new Promise((resolveAll, rejectAll) => {
        const rejectTimeout = setTimeout(rejectAll, 5000);
        Promise.all([
          new Promise(res1 => this._client.on('connected', res1)),
          new Promise(res2 => this._client.on('socketConnected', res2))
        ]).then(() => {
          clearTimeout(rejectTimeout);
          resolveAll();
        });
      });
    }
    

    【讨论】:

    • 如果在两个事件都发生时/如果您停止计时器,这不是更好吗?然后,您不会保持将被忽略的计时器运行,而是允许立即 GC 对象,而不是仅在计时器结束时。
    【解决方案2】:

    我是可重用代码的粉丝 - 如果你发现自己想要让 Promise 超时,那么为什么不编写一个函数来这样做

    const promiseTimeout = (promise, timeout) => {
        let timer;
        return Promise.race([
            promise, 
            new Promise((_, reject) => (timer = setTimeout(reject, timeout, promiseTimeout.symbol)))
        ])
        .then(result => (clearTimeout(timer), result));
    };
    promiseTimeout.symbol = Symbol('timeout');
    

    现在,您可以在需要为承诺设置时间限制的任何地方使用它

    在这种情况下,正如已经观察到的那样,您的逻辑需要 两个 承诺,包装在 Promise.all 中,这就是您的一个承诺,当两个承诺都解决时会解决

    const promise = Promise.all([
        new Promise(resolve => this._client.on('connected', resolve)),
        new Promise(resolve => this._client.on('socketConnected', resolve))
    ]);
    

    现在,结合这两个代码sn-ps

    const promiseTimeout = (promise, timeout) => {
        let timer;
        return Promise.race([
            promise, 
            new Promise((_, reject) => (timer = setTimeout(reject, timeout, promiseTimeout.symbol)))
        ])
        .then(result => (clearTimeout(timer), result));
    };
    promiseTimeout.symbol = Symbol('timeout');
    
    function connect() {
        const promise = Promise.all([
            new Promise(resolve => this._client.on('connected', resolve)),
            new Promise(resolve => this._client.on('socketConnected', resolve))
        ]);
        return promiseTimeout(promise, 5000);
    }
    
    // if connect rejects with reason promiseTimeout.symbol, you can be sure it was because of the timeout
    

    不过,在这种情况下,另一个答案更好,我只提供这个答案,以防你发现自己想要为承诺解决方案设置时间限制

    【讨论】:

    • 您可重复使用的超时不会因任何识别错误而被拒绝,这样用户就会知道是超时导致它被拒绝。这个概念很好,但实现有点短。
    • 简单代码示例太简单了 :p 基于 OP 的简单代码 - 仅为您修复它,@jfriend00 :p (这是您提出的非常有效的观点) - 可以提出同样的批评接受的答案:p
    • 我投了赞成票,但如果你想按照约定拒绝一个错误对象,这段代码并不容易。调用者必须预先创建一个在调用promiseTimeout() 时可能永远不会使用的错误对象。
    • 是的,它有我没有考虑过的缺点
    猜你喜欢
    • 2018-01-24
    • 2022-10-05
    • 2020-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 2019-02-11
    • 1970-01-01
    相关资源
    最近更新 更多