Different Approaches for Handling User Interactions in Angular Instead of Using the Deferred (Anti-?)Pattern

In the process of developing a game using Angular, I have implemented the following mechanics:

  1. An Angular service checks the game state and prompts a necessary user interaction.
  2. A mediator service creates this prompt and sends it to the relevant Angular component using an RxJS subject.
  3. The mediator service waits for a response to the prompt before allowing the game to proceed.
  4. The component captures the user's response to the prompt by calling the request.respond(response) method.

To meet these requirements, I needed to design a suitable Request class. Since requests are resolved definitively once initiated, I opted not to use RxJs Observable, choosing instead to utilize JavaScript Promise. Promises can be easily awaited using the async/await syntax, and the fourth requirement led me to explore the concept of the Deferred pattern. Consequently, I developed a base class for all types of requests as follows:

abstract class Request<T> {
  private _resolve: (value: T) => void = () => {};

  private _response: Promise<T> = new Promise<T>(resolve => {
    this._resolve = resolve;
  });

  public get response(): Promise<T> {
    return this._response;
  }

  public respond(response: T) {
    this._resolve(response);
  }
}

I chose not to include rejection handling since I did not foresee any scenarios in which the request would fail. Even a timeout mechanism seemed unnecessary, given that a response is essential for the game to progress.

While this approach adequately served my needs, I came across discussions labeling it as an anti-pattern (see, for instance, this discussion and this thread). Not being well-versed in working with promises, I am unsure about the potential risks associated with exposing the resolve function, the circumstances under which this pattern could be valid, and alternative methods of achieving my objectives using Promises.

Hence, I am seeking clarification on whether my implementation of the Deferred pattern is appropriate, and if not, what other approaches I could consider to accomplish the desired functionality.

Answer №1

One issue with the deferred antipattern lies in the exposure of the resolve function alongside or within the promise itself. Your request class doesn't necessarily need to contain the promise. A simpler approach would be to

const response = await new Promise(resolve => {
  mediator.send({ respond: resolve });
});

The mediator only requires this object, and the request-handling component can still use request.respond(response). This is more straightforward than

const request = new Request();
mediator.send(request);
const response = await request.response;

While the above example may seem overly complex (due to code within the Request class), it's not problematic yet. The real issue arises if you were to do

function sendRequest() {
  const request = new Request();
  mediator.send(request);
  return request;
}

as now someone has a "deferred object", rather than just a response promise. They could misuse the function:

const request = sendRequest();
request.respond("Ooops");
const response = await request.response;

The true danger lies in returning a deferred object to code that shouldn't be resolving the promise. It's fine to provide the resolve function to the intended responding component though.

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

What techniques can I use to adjust the size of an image through zooming in and out?

In my custom gallery component, the crucial code section looks like this: <Gallery> <Header> <img src={galleryIcon} alt='Galley icon' /> <h1>My Gallery</h1> </Header> ...

The AJAX request was successful, however, the PHP script did not return any

When using an Ajax success function to alert data in JavaScript, there may be occasions where the PHP side shows that the GET array is empty. <script type="text/javascript"> var myArray={ name:"amr", age:22 } myArray =JSON.stringify(myA ...

Is it possible to deactivate the click event on an Angular mat table row?

Within my Angular mat table, I have implemented code that expands a table row when clicked. However, I now need to prevent certain rows from being clickable based on the "element.disable" property. <ng-container matColumnDef="id"> <th mat-hea ...

Struggling to get this bootstrap carousel up and running

I can't seem to get this bootstrap carousel to switch slides, whether I use the indicators or arrows. My other carousel works perfectly fine and I've added all necessary Bootstrap and JavaScript CDNs. I'm puzzled as to why it's not func ...

Maximizing the potential of process.hrtime.bigint

Whenever I include the following code: const a = process.hrtime.bigint(); The linter says it's okay, but during compilation, I encounter this error message: error TS2339: Property 'bigint' does not exist on type 'HRTime'. This ...

What is the best way to ensure that JavaScript code is executed in a specific sequence?

Even though I understand that Javascript is not the same as C# or PHP, I keep encountering an issue with Javascript - specifically with how I use it. Here's the scenario: function updateStatuses(){ showLoader() //displays 'loader.gif' on ...

Buttons in Datatables fail to appear when using ajax loading

I've been trying to solve this issue for a while now, but I just can't seem to figure it out. According to the documentation, adding initComplete should make the buttons appear, but I am still facing difficulties. Am I overlooking something? I&a ...

Vue HeadlessUI Error: The function vue.defineComponent is not recognized

Trying to incorporate @headlessui/vue into my nuxt project has been a challenge. My attempt at using it looks like this: <template> <Menu> <MenuItems> <MenuItem>Item</MenuItem> </MenuItems> </Menu&g ...

Challenges encountered when redirecting users with a combination of javascript and php

I have a login form that triggers a JavaScript function upon submission. This function calls a PHP page to process the input. The issue I'm facing is with how the redirections are displayed based on the user's role type. It attempts to display t ...

Is there a way to remove specific items from my array in Vue.js?

Essentially, I am using an HTML select element that is populated with arrays of registered users. <label > <b> <i style="opacity:0.8">Users:</i> </b> </label>&nbsp;&nbsp; <select class=&quo ...

How can we stop the constant fluctuation of the last number on a number input field with a maxLength restriction

In my ReactJS code, I have an input field that looks like this: <input type="number" onKeyPress={handleKeyPress} maxLength={3} min="0" max="999" step=".1" onPaste={handlePaste} /> Below are the functions associated w ...

Navigate to the identical component using Angular

There is a situation where a user visits /details;id=1. In the DetailsComponent, there is a table displaying similar objects with a button that redirects to /details;id=x, where x represents the id of the object. After clicking on the button, the URL para ...

Issues with AJAX functionality not operating properly in a web form, leading to automatic redirection to a PHP script. Possible solutions and troubleshooting

I have a feature on my web application that allows users to add their skills by clicking the "Add a Skill" button. It was working fine until I tried to incorporate something new. The issue is that when I click the "Add a Skill" button, it acts as though it ...

Unable to establish a connection with Metamask

Looking to connect to Metamask and retrieve the account balance: <!DOCTYPE html> <html> <head> <title>Testing Ethereum with Metamask</title> <meta charset="UTF-8"> <meta name=&quo ...

A guide to updating directives on the fly in Angular 2

Currently, I am using Angular 2+ along with Material 2. In my project, I have a few md-button elements that I need to dynamically change to md-raised-button. To clarify, this is what I aim to do: <a md-button [routerLink]="['/home']">Hom ...

Tips for removing "<li>" elements using JavaScript

My issue remains unresolved... I created a tree structure in MVC and added JavaScript code for expanding and collapsing the tree. However, after applying the code, my tree is not being displayed correctly. enter image description here In the image, you c ...

In terms of function efficiency, what yields better results: using conditional execution or employing conditional exit?

Feedback is welcomed on the efficiency of the following JavaScript/TypeScript solutions: function whatever(param) { if (param === x) { doStuff(); } } or function whatever(param) { if (param !== x) { return false; } doStuff(); } The se ...

What is the best way to combine API calls using rxJs subscribe and map in Angular?

Currently, I am executing multiple API requests. The first one is responsible for creating a User, while the second handles Team creation. Upon creating a User, an essential piece of information called UserId is returned, which is crucial for the Team cre ...

AngularJS Interceptors for secure page management

I recently started working with AngularJS and I'm facing an issue with my interceptor that catches 401 errors from server responses. When a 401 status is detected, it triggers a "loginRequired" message broadcast and redirects to the login page. Howev ...

Is it possible to import the identical file twice consecutively using html and typescript?

I encountered an issue with an input element in my HTML file. Here's what it looks like: <input type="file" (change)="receiveFile($event)" id="inputFileButton" hidden /> This input element is designed for users to import files. Wh ...