Fulfilling a pledge from an external source to postpone the implementation of the program

In my Angular service, there is an object called _mapView that gets initialized after the entire application and its dependencies are loaded. However, there is a possibility that users might interact with buttons or other elements triggering get-calls to this mapView object before the app is fully loaded, as the mapView loads asynchronously.

My aim is to ensure that if the object is not initialized yet, the program will wait until the mapView is ready.

@Injectable()
export class MapService {
   private _mapViewPromiseResolveFx;
   private _mapView: Promise<__esri.MapView> = new Promise(function(res, rej){
       this._mapViewPromiseResolveFx = res;
   }.bind(this)); // saving a reference to the resolve function allows the promise to be resolved externally

   public resolveMapView(mapView: __esri.MapView) {
      this._mapViewPromiseResolveFx(mapView);
   }

   public getMapView() {
      return this._mapView;
   }
}

At some point during the initialization of the AppComponent, I initialize this variable:

self.mapService.resolveMapView(new MapView(mapViewProperties));

Therefore, whenever I require the mapView, I use:

this.mapService.getMapView().then(function(mapView) {

Although this method appears to work, it feels like a somewhat clumsy approach and potential misuse of promises. Are there any other, more optimal solutions available?

Answer №1

The concept of the Deferred pattern is utilized to handle asynchronous operations. While originally introduced in jQuery, it is sometimes considered an antipattern but has its own advantages.

In essence, a deferred object can be returned instead of just a promise, similar to how the current MapService functions. However, there will be a centralized deferred object responsible for managing its associated promise:

class Deferred<T = {}> {
  resolve!: (val: T) => void;
  reject!: (err: any) => void;
  promise = new Promise<T>((resolve, reject) => {
    this.resolve = resolve;
    this.reject = reject;
  });
}

@Injectable()
export class MapService {
   mapView = new Deferred<__esri.MapView>();
}

Given that this is an Angular-related query, it's important to note that RxJS plays a crucial role in Angular applications. It serves as a more versatile alternative to promises and offers additional functionalities. A counterpart to promises and deferreds in RxJS is AsyncSubject, which emits a single value once the subject is completed.

Hence, using RxJS:

@Injectable()
export class MapService {
   mapView = new AsyncSubject<__esri.MapView>();
}

To resolve it:

mapService.mapView.next(...);
mapService.mapView.complete();

If needed as a promise, AsyncSubject can smoothly transition to a promise as it completes with a single value:

const view = await mapService.mapView.toPromise();

Both observables and promises can be managed within views using the async pipe like so: {{ mapService.mapView | async }}.

Although functional, this approach may seem unconventional and bordering on misuse of promises.

Misuse of promises can lead to antipatterns, much like the case with deferreds.

An Angular service should ideally serve as the model component in the MV* architecture. Therefore, MapService should not receive map data externally but rather initialize and store map data internally. It should instantiate MapView during application startup and provide a promise of the MapView instance.

Answer №2

While it is technically allowed, it is generally not recommended to perform I/O implicitly.

There are a couple of key points that should be addressed:

  • Ensure you include this._mapView.catch(() => {}) to prevent unhandled rejections from appearing (it is best practice to always handle rejected promises).
  • Performing I/O operations in the constructor or during initialization can be confusing and may violate the principle of least astonishment.

Consider creating a static async method like init to initialize the promise on first call, allowing consumers to handle any potential errors.

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

Child component in Angular fails to recognize changes

When I update the product object from the parent component, the child component does not seem to detect the change and fails to update accordingly. product.ts export interface Product { productId: number; productName: string; lastUpdated: str ...

Jest encountering errors when compiling a generic function

I'm able to successfully run my Node app, but running tests on a class with Generics is causing an error. The test code looks like this: import { Request, Response } from 'express'; import { JsonWebTokenError } from 'jsonwebtoken' ...

Unlocking The Mystery of Disappearing Inputs on Ionic 5

How can I prevent Ionic 6 (Angular) from hiding inputs when the keyboard shows? Whenever I focus on an input, the keyboard covers it. Is there a way to automatically scroll so the keyboard is positioned below the selected input? View Image of Input/Keyboa ...

Express-openapi routes are giving a 404 error

As a beginner in the world of openapi, I have been attempting to transition an existing express application to work with openapi. After diligently following the provided documentation, I decided to convert one of my routes to openapi to test it out (disab ...

What are the appropriate token classifications for Dependency Injection (DI)?

Back in the days of Angular 1, providers only accepted strings as tokens. However, with the introduction of Angular 2, it seems that class tokens are now being predominantly used in examples. Take for instance: class Car {} var injector = ResolveInjector ...

Ensure that every member of an object adheres to a particular generic interface in some capacity

Can a TypeScript index signature type be used to enforce that every index adheres to a generic interface? I want to define code like this: interface HasState<T> { state : T; } interface ModuleDefinition { [moduleName : string] : <T>HasSta ...

The error page is requesting a root-layout, which indicates that having multiple root layouts is not feasible

My issue is as follows: The not-found page located in my app directory requires a root-layout which I have added to the same directory. However, this setup prevents me from using multiple root layouts in the structure below. How can I resolve this? It see ...

What is the best way to send a user-provided input to my cloud function?

Currently, I am delving into the realm of Cloud Functions for Firebase and have successfully implemented an auth trigger by following a tutorial. However, I am now facing a challenge in passing the username that the user desires to use to my auth event. ...

Utilize node.js to extract parameters from various parts of the chain and maintain a linear workflow

Is it possible to maintain clean code while fetching -paramFromA and paramFromB in this way? All of the functions included here return a new Promise. var a = helper.getAccountPromise(tokens); var b = a.then(helper.getFundingPromise) var c = b.then(h ...

Learn the process of binding data to formGroup that utilizes the @propArray decorator in Rxweb

Recently, I decided to utilize the rxweb-reactive-form-validators library for my current project. Despite scouring every possible resource, I couldn't find a solution to my query. Therefore, I am presenting it here in hopes of receiving assistance. ...

Show real-time data using Angular6 and GoogleChart

In my project, I am utilizing Angular Cli6, angularfire2, and Firebase to create a timeline using GoogleChart. //GoogleChart.service declare var google: any; export class GoogleChartsBaseService { constructor() { google.charts.load('current&apo ...

Code remaining stable post-execution of promise

I'm facing a problem with my Node.js application where I'm using promise-mysql and bluebird packages to make calls to a MySQL database. Despite following tutorials and successfully querying the database, I keep encountering a timeout error. The p ...

How to dynamically add a route from an HTTP API to the app-routing.module.ts file in Angular 10

Struggling with an issue while working on Angular 10 version. Despite finding numerous solutions for older versions of Angular, I am still unable to resolve the issue. I consider myself a beginner in Angular as I try to create a website using an Angular c ...

Not verifying the argument type in Typescript makes the function generic in nature

I was initially under the impression that TypeScript would throw an error due to passing the incorrect number of elements in EntryPoints, but surprisingly, no error occurred. function createContext<T>(defaultValue: T): T[] { return [defaultValue] ...

Support for ViewEncapsulation.ShadowDom now available in Edge, Internet Explorer, and legacy browsers

I am working with Angular 7 and material design. Some of my components utilize ShadowDOM ViewEncapsulation, leading to errors in older versions of IE, Edge, Chrome, and Firefox. Below is the error message I am encountering: Object doesn't support pr ...

What is the best way to limit the types of function parameters in TypeScript based on whether the parameter index is even or odd?

My goal is to create a function with an unlimited number of parameters, where the type of each parameter is determined by whether its index is odd or even. For example: flow(isMachineReady(), 'and', isWaterHot(), 'or', isMilkHot(), &ap ...

Guide on correctly importing the Monaco IStandaloneCodeEditor interface in Vue3 using the Composition API

While working with Monaco, I came across a challenge in defining the instance type for encapsulating a component package. I am unsure about how to import the interface IStandaloneCodeEditor | IStandaloneDiffEditor, or if it is necessary at all. <script ...

Leverage es6 classes along with mongoose in typescript to utilize loadClass functionality

I've been struggling with a problem and despite my exhaustive search on Google, I still haven't found a solution. My issue revolves around incorporating es6 classes with mongoose using the schema.loadClass(class) method. Unfortunately, when worki ...

Having trouble executing Angular e2e tests above version 2 with protractor while using a proxy

Seeking assistance and guidance! Operating on a Windows system Globally installed Protractor Version 5.3.0 Ran webdriver-manager clean before updating webdriver Updated version with the following command: webdriver-manager update --ie32 --proxy ...

Progress bar in Windows Forms updating as the system executes a task in the background asynchronously

How can I create an onclick event using Windows Forms that can simultaneously run two functions independently from each other? One function involves reading a sim card and the other function renders the application unresponsive for 5000 milliseconds. priva ...