Filter the output from a function that has the ability to produce a Promise returning a boolean value or a

I can't help but wonder if anyone has encountered this issue before.

Prior to this, my EventHandler structure looked like:

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

Initially, everything was working smoothly with my filter function and tests passing while filtering events using:

messages.forEach(message => {
      const event: EventEntity = JSON.parse(message.Body);
      this.handlers
        .filter(handler => handler.canHandleEvent(event)) // Everything was working fine
        .forEach(handler => {
           // Logic
        });

However, we recently had to make a change to the canHandleEvent method to either return Boolean or Promise. This modification was necessary because certain promises needed resolution in order to determine whether the event could be handled or not.

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

To address this shift, I employed Promise.resolve and Promise.all, but unfortunately:

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 => {

Currently, the tests are now passing for the Promise-based canHandleEvent, but they are failing for Boolean returns. Here is an example of one such failing test scenario:

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);
    });
  }
}

Some previously passing tests that are now failing include:

    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);
    });

Any suggestions on how to tackle this challenge? I've attempted to differentiate between promises and booleans within the .filter function with no success:

      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)
        })

Answer â„–1

Presently, there has been a necessity to modify the canHandleEvent function to either return a Boolean value or a Promise...

To clarify, this is a significant semantic alteration that will impact every layer of code utilizing this method. Using filter directly with it is no longer possible, and any synchronous function that relies on it may now become asynchronous (where essentially, "potentially asynchronous" equates to "asynchronous"). However, if this change is imperative, then it must be done! :-)

Your original code implementation with canHandleEvent looked like this:

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

Now, it needs to be made asynchronous as shown below:

// Processes actions concurrently, not sequentially
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
        }
    }));
}));

You can observe how each component was impacted. The use of messages.forEach transitioned into constructing an array of promises via messages.map and waiting for their completion using await (or utilizing .then, etc., or returning to a calling function). Similar processing occurs for handlers within each message since it's uncertain whether a handler can handle something synchronously. (No need for Promise.resolve, as Promise.all manages that process.)

The provided code assumes parallel execution of these tasks (both message handling and handler processing), whereas previously, due to synchronous operations, they took place in sequence (all relevant handlers for one message followed by all handlers for the next message, and so forth). If a sequential approach is required, you can utilize for-of loops instead:

// Handles things in series, not parallel
// (Within 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
        }
    }
}

In both scenarios, handling functions returning a boolean differently from those returning promises is feasible, but it does add complexity to the code.

Answer â„–2

In order to address your problem, I believe the most straightforward approach is to initially populate the array with the expected values so that you can effectively filter.

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

This action will alter the array to include a new key indicating which handlers are capable of being handled.

To complete this process, utilize your code as usual but instead of checking

canHandleEvent

refer to the new field introduced in the const updatedHandlers

here's an example:

updatedHandlers
.filter(handler => handler.eventHandled)
.forEach(handler => {
   // PROCEED WITH LOGIC
});

These steps should maintain the functionality of your code as intended.

I apologize for any language errors, English is not my native tongue. Thank you.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Editing an XML file on the browser and saving it in its original location on the local file system

Seeking assistance with editing an XML file directly from the browser on a local file system and saving it back to its original location. I have conducted extensive research using Google, but have not been able to find a suitable solution. Any help or gu ...

Http provider not found in Angular 4 when using Rails 5 API

I recently completed a tutorial on Angular and Rails which I found here, but I am encountering difficulties in implementing it successfully. Currently, I am working with Angular version 4.2.4. [Error] ERROR Error: No provider for Http! injectionError — ...

There was a type error that occurred because the property 'addEventListener' could not be read from an undefined value

Encountering an issue with the JavaScript libraries three.js and OrbitControls.js despite following a tutorial: However, an error is being displayed in the console: Uncaught TypeError: Cannot read property 'addEventListener' of undefined at ne ...

Encountering Issues with TypeScript Strict in Visual Studio Code Problems Panel

I have discovered that I can optimize my TypeScript compilation process by utilizing the --strict flag, which enhances type checking and more. Typically, I compile my TypeScript code directly from Visual Studio Code with a specific task that displays the c ...

How do I create a sliding dropdown notification bar?

Can anyone provide some guidance on how to create a sliding dropdown section for my homepage, similar to the one on this website: . (Please note, be cautious of potential malware) I'm interested in replicating the dropdown section that says "call for ...

The v-list-group does not automatically expand sub-groups based on the path specified in the group prop

I have a navigation sidebar that includes nested v-list-groups. Based on the documentation, the "group" prop of v-list-group should expand based on the route namespace. To see more information, visit: https://vuetifyjs.com/en/components/lists/ While this ...

Switch between playing and pausing the mp3 audio in the Next application by using the toggle

I am currently working on my website and I have been trying to add a button to the bottom of the page that can play or pause a song using useSound library. The song starts playing when I click it for the first time, however, I am facing difficulty in stopp ...

Using the Async feature, I can retrieve the value of a string type when the return type of a function is Promise<any>

While working on a custom pipe, I encountered a situation that puzzled me. Can you explain why the code snippet below is considered valid? async transform(value: any): Promise<string> { let fullNameBasedOnPreference: string; fullNameBasedOnP ...

Troubleshooting: Clicking the Ajax button yields no response

I’ve looked through all the similar questions and tried multiple solutions, but nothing seems to be working. My goal is to retrieve job postings from my database related to careers. When a user wants to apply for a job, they should click a button which w ...

Managing file sizes on the client side prior to transferring them to the server side

I am looking to implement client-side validation for file size. The current code only handles success, but I also want to address file size limitations. Right now, if a file exceeds 2MB, it results in a 500 (Internal Server Error) behind the scenes. Is th ...

Tips for looping through nested JSON data in Google Sheets

In my attempt to iterate through the nested "line_items" element in a JSON response triggered by a post event, I aim to populate a Google Sheets spreadsheet using Google Apps Script. My objective is to write the elements within each "line_item" into new ro ...

An issue arises when attempting to fetch data using next.js: a TypeError occurs indicating that

I'm currently working on setting up a next.js website integrated with strapi, but I've encountered an issue with my fetch request. Oddly enough, when I test the request using Postman, the data is successfully returned, indicating that the endpoin ...

Mapping distinct JSON keys to button components using Javascript/React

I am dealing with the JSON data below: [ {"location":"2034","type":"Residential","price":400000,"address":"123 Fake Street","suburb":"Maroubra","historical_DAs&q ...

Guide on using the backbone.js save() method to upload multiple files post-form submission

I am currently utilizing Backbone as my JavaScript framework. I have a form where I need the capability to upload up to 10 images simultaneously. To achieve this, I am setting the enctype attribute to "multipart/form-data" in the form tag and including the ...

A guide to displaying the date retrieved from a JSON data file API request using JavaScript

How can I display the date from a JSON data file API call using JavaScript? I am facing difficulty in showing the date on the dashboard display page. I am utilizing JavaScript with async-await for calling the API, and Bootstrap 4 for design. The function ...

JavaScript/Typescript is throwing an error because it is unable to access the property 'username' of an undefined object

In my project, I am attempting to compile a list of Profile objects and then extract specific elements from each object in the list. To accomplish this, I have defined a public interface named Profile, imported it into my component, and instantiated a new ...

Is there a way to create a universal getter/setter for TypeScript classes?

One feature I understand is setting getters and setters for individual properties. export class Person { private _name: string; set name(value) { this._name = value; } get name() { return this._name; } } Is there a w ...

Displaying a div when a button is clicked (PHP)

Hello everyone, I'm new to posting here so please bear with me if I seem inexperienced. On my website, there is a button that needs to disappear when clicked and be replaced by a form without refreshing the page. I was able to accomplish this using ...

Detection of NTLM error during the parsing of the Authorization header

After posting a question here, I discovered that the issue causing the error was not related to my axios request. Unfortunately, I'm unsure how to close the question. An error occurs when attempting to send an Axios Get request to my express backend ...

Dynamic Visibility Control of GridPanel Header in ExtJS

I have a GridPanel that needs to display a limited number of resources. If there are more resources available than what is currently shown, I would like the panel's header/title to indicate this by displaying text such as "more items available - displ ...