【问题标题】:Javascript - Filter result from function that can return Promise boolean OR booleanJavascript - 从可以返回 Promise 布尔值或布尔值的函数中过滤结果
【发布时间】:2021-10-21 07:42:11
【问题描述】:

我想知道以前是否有人遇到过这个问题。

在我有一个看起来像这样的 EventHandler 之前:

export interface EventHandler {
  name: string;
  canHandleEvent(event: EventEntity): boolean;
  handleEvent(event: EventEntity): Promise<void>;
}

我的过滤器功能可以正常工作,我的测试也通过了 - 我使用以下方法过滤事件:

messages.forEach(message => {
      const event: EventEntity = JSON.parse(message.Body);
      this.handlers
        .filter(handler => handler.canHandleEvent(event)) // WORKED WELL
        .forEach(handler => {
           // LOGIC
        });

目前,我们必须将 canHandleEvent 更改为布尔值或 Promise。因为我们有一些承诺要解决并确定事件是否可以处理。

export interface EventHandler {
  // ...
  canHandleEvent(event: EventEntity): boolean | Promise<boolean>;
}

所以,为了解决这个问题,我使用了Promise.resolvePromise.all。运气不好:

messages.forEach(async message => {
      const event: EventEntity = JSON.parse(message.Body);
      const handlersResolved = await Promise.all(this.handlers);

      handlersResolved
        .filter(handler => handler.canHandleEvent(event))
        .forEach(handler => {

现在,我对 Promise canHandleEvent 的测试通过了,但对于通过的事件 boolean,它们却失败了。它们看起来像这样:

class HandlerB implements EventHandler {
  name = HandlerB.name;
  numRuns = 0;

  canHandleEvent(event: EventEntity): boolean {
    console.log('event', event)
    return event.eventType === EventType.ONE_EVENT || event.eventType === EventType.SECOND_EVENT;
  }
  async handleEvent(event: EventEntity): Promise<void> {
    return new Promise(resolve => {
      setTimeout(() => {
        this.numRuns += 1;
        resolve();
      }, 25);
    });
  }
}

我现在失败和之前通过的测试是:

    it('Should process handlers that match, including canHandleEvent that returns Promise<boolean> TRUE', async () => {
      setHandlers([handlerA, handlerB, handlerC]);

      const event = await createEvent(EventType.SECOND_EVENT);
      await sleep(1000);

      expect(handlerA.numRuns, 'handleA').to.eql(0);
      expect(handlerB.numRuns, 'handleB').to.eql(1);
      expect(handlerC.numRuns, 'handleC').to.eql(1); // handlerC is Promise<boolean>, it works fine
      expect(errorHandler.numRuns).to.eql(0);

      handlerC.numRuns = 0;
    });

    it('Should allow handlers to pass even if one has an error', async () => {
      setHandlers([handlerA, handlerB, errorHandler]);

      const event = await createEvent(EventType.USER_REGISTRATION_STATUS);
      await sleep(1000);

      expect(handlerA.numRuns, 'handlerA').to.eql(1);
      expect(handlerB.numRuns, 'handlerB').to.eql(1);
      expect(errorHandler.numRuns, 'errorHandler').to.eql(1);
    });

关于如何解决这个问题的任何想法?我尝试在.filter 之前确定是promise 还是boolean,但仍然没有运气:

      this.handlers
        .filter(async handler =>  {
          if(typeof handler.canHandleEvent(event).then == 'function') {
            const result = await Promise.resolve(handler.canHandleEvent(event))
            console.log('IS PROMISE!!', result);
            return result
          }
          console.log('IT IS NOT PROMISE', handler.canHandleEvent(event))
          return handler.canHandleEvent(event)
        })

【问题讨论】:

    标签: javascript typescript promise


    【解决方案1】:

    为了解决您的问题,我认为最简单的方法是首先使用预期值填充数组,以便您可以正确过滤。

    const transformedHandlers = await Promise.all(this.handlers.map(async handler => {
       return {
          ...handler,
          eventCanBeHandled: await handler.canHandleEvent(event)
       }
    }))
    

    这将转换数组,以便您拥有一个显示可以处理哪些处理程序的键。

    要完成它,您可以像往常一样使用您的代码,而不是检查

    canHandleEvent

    您使用 const 转换处理程序中引入的新字段

    下面是例子:

    transformedHandlers
    .filter(handler => handler.eventCanBeHandled)
    .forEach(handler => {
       // LOGIC
    });
    

    这应该足以让您的代码像以前一样工作。

    对不起我的英语。这不是我的母语

    【讨论】:

      【解决方案2】:

      目前,我们必须将 canHandleEvent 更改为 Boolean 或 Promise...

      需要明确的是,这是一个巨大的语义变化,将波及使用该方法的代码的每一层。例如,您不能再直接将filter 与它一起使用,并且任何使用它的同步函数现在都可能是异步的(从根本上说,“潜在异步”=“异步”)。但如果它必须发生,它必须发生! :-)

      您使用canHandleEvent 的原始代码如下:

      messages.forEach(message => {
          const event: EventEntity = JSON.parse(message.Body);
          this.handlers
              .filter(handler => handler.canHandleEvent(event)) // WORKED WELL
              .forEach(handler => {
                   // LOGIC
              });
      });
      

      必须变成异步的,像这样:

      // Handles things in parallel, not series
      await/*or return*/ Promise.all(messages.map(message => {
          const event: EventEntity = JSON.parse(message.Body);
          return Promise.all(this.handlers.map(handler => async {
              if (await handler.canHandleEvent(event)) {
                  // LOGIC
              }
          }));
      }));
      

      注意每一层是如何受到影响的。 messages.forEach 变成通过messages.map 构建一个promise 数组并通过await 等待它们(或使用.then 等,或返回调用函数)。对于每条消息,我们对处理程序做同样的事情,因为我们不知道处理程序是否可以同步处理某些事情。 (不需要Promise.resolvePromise.all 会为您处理。)

      上面的代码假设所有这些都可以重叠(消息和消息的处理程序),而之前因为都是同步的,所以它们都是连续发生的(一条消息的所有相关处理程序,按顺序,然后是下一个的所有处理程序,等等)。如果你需要它像这样串联,你可以使用for-of循环:

      // Handles things in series, not parallel
      // (In an `async` function)
      for (const message of messages) {
          const event: EventEntity = JSON.parse(message.Body);
          for (const handler of this.handlers) {
              if (await handler.canHandleEvent(event)) {
                  // LOGIC
              }
          }
      }
      

      在这两种情况下,可以以不同方式(同步)处理返回 boolean 和返回 Promise 的那些,但这会使代码复杂化。

      【讨论】:

      • 这是一个非常完整的答案,非常感谢您提供的所有详细信息
      猜你喜欢
      • 2013-03-11
      • 1970-01-01
      • 2017-06-07
      • 2011-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-29
      • 1970-01-01
      相关资源
      最近更新 更多