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

Accessing data in form rules with Vuetify: tips and tricks

Is it possible to access a data element within a rule? Click here to see my code in action I'm attempting to change the value of a data element on a text field rule within a Vuetify form. While the rule itself is functioning properly, I'm enco ...

Activate the event when the radio button is changed

Is there a way to change the selected radio button in a radio group that is generated using a for loop? I am attempting to utilize the changeRadio function to select and trigger the change of the radio button based on a specific value. <mat-radio-group ...

The suspense fallback function seems to be missing in NextJS 13

I'm in the process of creating an application to demonstrate the functionality of Suspense in Nextjs 13. However, I'm encountering an issue where the Suspense fallback is not appearing during loading. Below is the code for page.js import React, ...

React : understanding the state concept as written like ...state

As I explore React, can you please explain the significance of this piece of code to me? const new_venues = this.state.venues.map((venue) => place_id === venue.place_id ? { ...venue, open : !venue.open } : { ...venue, open: false }); I understand the ...

Node.js application experiences a delay when calling Mongoose Model.save()

I've been delving into the realms of node and mongo in order to construct a basic web application while also expanding my knowledge on web development. However, I'm encountering an issue when it comes to calling Model.save(); the continuation fun ...

Is it possible to utilize jQuery's .wrap or .wrapInner to encase a variety of elements within them?

I am working with a HTML structure that looks like this: <section> <item> <ele class="blue" /> <ele class="green" /> <ele class="red" /> </item> <item> <ele class="blue" /> <ele ...

Unusual class title following npm packaging

Currently, I am working on developing a Vue 3 library with TypeScript. We are using Rollup for bundling the library. Everything works as expected within the library itself. However, after packing and installing it in another application, we noticed that th ...

Change the behavior of a submit button to trigger a custom JavaScript function instead

I am faced with a challenge where I need to override the default functionality of a button in code that cannot be altered. Instead, I must ensure that when the button is clicked, a custom JavaScript method is called rather than submitting the form as it no ...

Tips on creating a responsive absolute div

I'm currently working on creating a profile component with Material UI and React.js. I'm having trouble making the Avatar/profile photo and profile name div responsive. Here are some screenshots to illustrate my issue: The specific div that need ...

Is it possible for me to identify the original state of a checkbox when the page first loaded, or the value it was reset to when `reset()` was

When a webpage is loaded, various input elements can be initialized as they are declared in the HTML. If the user modifies some of the input values and then resets the form using reset(), the form goes back to its initially loaded state. I'm curious, ...

What are the steps to create a circular progress bar in an Ionic 3 application?

Can anyone help me create a circular progress bar in Ionic 3? I'm new to Ionic and have already attempted to install the jQuery circle progress package by running npm install jquery-circle-progress. The installation was successful, but now I'm un ...

Tips for creating a concise summary of written content

I am interested in creating an AI-powered summary generator for text input within a textarea element. Below is the HTML code snippet I have been working with: <textarea id="summary">Enter your text here</textarea> If you hav ...

Avoid reloading the page when submitting a form using @using Html.BeginForm in ASP.NET MVC

I have a webpage featuring a model that needs to be sent to the controller along with additional files that are not part of the model. I have been able to successfully submit everything, but since this is being done in a partial view, I would like to send ...

Retrieve progress with easing using jQuery's animate() function

At the moment, I'm utilizing this code to create an animation for a bar-graph-inspired element: $('.anim').animate({ right: `${100-(e/max*100)}%`, backgroundColor: colors[0] },{ duration: 1500, easing: 'easeInQuart&apos ...

How to host an Angular 2 application on IIS-10 without using ASP.NET Core

I've managed to create a plane angular-2 application using webpack in Visual Studio 2015 and now I'm looking to deploy it on IIS-10. Since I didn't use the ASP.NET Core template for this project, I need guidance on how to properly deploy thi ...

Exploring unique forms with the turfjs polygon difference() function

While implementing the difference() function for my polygon map, I encountered a problem where unexpected shapes would appear or disappear when zooming in on the map. These shapes should not be there. The polygons involved are of type MultyPolygon and Poly ...

Having trouble importing .task files in a Next.js project with TypeScript?

I encountered an issue when trying to import a model.task file into my App.tsx file. After training a hand gesture recognition model in Python, I exported it to a model.task file. Now, I am attempting to import this file into my Next.js + Typescript proje ...

My CSS files are not being included by grunt-bower-install

As someone who is relatively new to bower and grunt, I'm struggling with some basic tasks. After running bower install --save bootstrap, my goal is to use grunt-bower-install to update my js & css files as per the instructions on their documentat ...

Express app does not receive HTTP GET parameter

When making the GET request, the response received is a 404 error code. Upon sending the request to http://localhost:3000/api/watchings/?watchItemUniqueKey=5dab6083b68bd049c0b8f9ce%7C5daf5e0c8d261e5364acd8b6, the server responds with a 404 Not Found err ...

What could be causing the lack of re-rendering in children components using redux-form?

When the parent component sends data, the children components do not re-render automatically. Re-rendering only occurs when a key is pressed on an input element. SMART User values from the state are sent by the smart component. If we add console.log(this. ...