Is it possible for Angular/Typescript promises to result in unordered execution?

We are currently grappling with a unique challenge in our Angular application that we inherited. The issue revolves around a system where a digital input is meant to control both the backlight and the displayed page. Essentially, if the digital input signal is activated, the backlight should illuminate, allowing for normal user interaction with the application.

However, when the digital input signal is inactive, the application should present a blank screen and turn off the backlight entirely, eliminating any possibility of blind user interaction with an unintended page.


The backend process manages the backlight functionality and communicates information to the frontend (both processes run on the same device) via Angular routing. Nonetheless, the frontend retains full autonomy over what content is displayed on the web page.

Our current observation reveals that toggling the digital input signal rapidly sometimes results in the backlight turning on while displaying a blank page. This occurrence prompts us to consider whether there might be a race condition within the event delivery mechanism operating through the router.


In terms of code implementation, we have an effect that monitors the digital input signal status from the backend indirectly, resembling the following snippet:

@Effect({ dispatch: false })
receiveDigitalInputEvent$ = this.actions.pipe(
  ofType(AppActionTypes.RECEIVE_DIGITAL_INPUT),
  map((action: AppActions.ReceiveDigitalInput) => action.payload),
  withLatestFrom(this.store.pipe(select(getRouterState))),
  tap(([data, routerState]) => {
    const currUrl = routerState ? routerState.state.url : '';
    const { isDigitalInputSet, someOtherStuff } = data;
    this.forceNavigateIfNeeded(isDigitalInputSet, currUrl);
  })
);

forceNavigateIfNeeded(isDigitalInputSet: boolean, currUrl) {
  if (isDigitalInputSet && currUrl.endsWith('/blank')) {
    this.router.navigateByUrl('/welcome');
  else if (! isDigitalInputSet && ! currUrl.endsWith('/blank')) {
    this.router.navigateByUrl('/blank');
  }
}

Essentially, upon receiving a digital input event:

  • If the input signal is active and the screen is blank, the application navigates to the welcome screen.
  • If the input signal is deactivated and the current screen is not the blank one, the application switches to the blank screen mode.

Albeit functioning correctly under normal circumstances, the fast transitions seem to trigger anomalies.


While my familiarity with Angular internals is limited, I suspect that incoming events may be queued for routing delivery as part of the system's mechanism (especially considering the backend pushes data to the frontend using web sockets).

Upon inspecting the Angular source code, I noted that navigateByUrl operates using promises, potentially enabling the code to proceed to the next message queue item before the page change takes effect. Seeking validation or correction of my assumptions, I suggest the following scenario:

  1. The digital input deactivates, prompting the backend to dispatch an "OFF" message to the frontend and disable the backlight.
  2. The digital input reactivates swiftly, signaling the backend to send an "ON" message back to the frontend and re-enable the backlight.
  3. The frontend processes the "OFF" message and, since it detects being on a non-blank page, triggers a navigateByUrl('/blank') operation which initiates a promise.
  4. Before the aforementioned promise fulfills (and the routerState.state.url transitions from non-blank to blank), processing of the "ON" message begins. Consequently, with an active "ON" message but a stale non-blank page, the forceNavigateIfNeeded() function remains dormant.
  5. The ongoing navigateByUrl('/blank') promise concludes, resulting in the actual transition to a blank page.

This sequence reproduces the problematic situation described previously, characterized by a blank screen illuminated by the backlight.

Is such behavior feasible within the Angular/Typescript framework? It appears to hinge on the rapid queuing and execution of incoming messages in a single-threaded fashion alongside potential delays in front-end navigation timing and page state updating.

I am seeking guidance on the plausibility of this scenario and possible solutions to address it efficiently.

My conjecture is that pausing within forceNavigateIfNeeded() to await promise completion may not be advisable, but I remain unsure about alternative approaches.

Answer №1

While the issue you're facing may be intricate, there seems to be a suspicious code snippet that warrants closer inspection.

...
withLatestFrom(this.store.pipe(select(getRouterState))),
...

This particular code segment could potentially be the root cause of your problem as it simply takes what's available at the moment without waiting for updates. I encountered a similar issue before where milliseconds were crucial, and my workaround involved replacing withLatestFrom with the switchMap operator. It might be worth trying out in your scenario.

map((action: AppActions.ReceiveDigitalInput) => action.payload),
switchMap(data => //data === action.payload
 this.store.select(getRouterState).pipe(map(routerState => ({ data, routerState }))
),
tap(({data, routerState}) => { ... }

EDIT: In our case, we utilized a custom selector within the switchMap function to compare current and previous states and return the comparison result. While it worked for us, I can't guarantee its effectiveness in your situation, but it was vital for solving our puzzle.

export const getFeshRouterState= pipe(
  select(getRouterState),
  startWith(null as RouterStateInterface),
  pairwise(),
  map(([prev, curr]) => /* do some magic here and return result */),
);

Apologies for any initial confusion.

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

Display jqgrid with borders and insert extra headers text at the top of the grid

function generateTableWithText(){ $("#active_grid").jqGrid("exportToHtml",{ includeLabels : true, includeGroupHeader : true, includeFooter: true, autoPrint : true ...

Using CDN to load the STLLoader in Three.js

After deciding to have some fun by creating an STL loader, I've hit a roadblock. Despite trying various solutions found online, I'm still facing issues, mainly due to CDN errors. Currently, I'm following the tutorial on the Three.js site and ...

Manipulating Select2 without using jQuery

Is there a way to manage the select2 component programmatically without using jQuery? I need to execute this code through Selenium, and since I cannot access the jQuery object (as it's bundled with Webpack), I have to control it using pure JS. I atte ...

What are the steps to deactivate data from a select option in Vue?

If a customer's status is marked as deceased, I need to prevent the selection of "healthy", "little healthy", and "unhealthy" options. How can I adjust the checkHealth method accordingly? <template v-for= "customer in customers"> < ...

Attempting to retrieve and substitute the final value within an input field by cross-referencing or evaluating it against a different value

Can you help me with a solution for fetching the last value (word or character) in an input box, storing it in a variable, and then replacing it with one of a set of values that match exactly or partially? The items within the bindname-block contain vario ...

Issue encountered while constructing an array within the component data following the 'created' event

It's been a while since I've dealt with Vue.Js, so my memory is a bit fuzzy. I'm facing an issue where I can't seem to modify a data array within a function that is called in the created hook. My goal is to create a multidimensional ar ...

ASP.NET MVC controller encountering internal server error 500 during Ajax request

I have a scenario where I need to populate two dropdowns in a view: <select class="input-sm form-control input-s-sm inline" onchange="loadCities()" id="comboState"> ... </select> <select class="input-sm form-control input-s-sm inline" ...

Ensuring the correct class type in a switch statement

It's been a while since I've used Typescript and I'm having trouble remembering how to properly type guard multiple classes within a switch statement. class A {} class B {} class C {} type OneOfThem = A | B | C; function test(foo: OneOfThe ...

Number of rows clicked in Kendo Grid

Is there a way to calculate the clicked row count in a Kendo grid without including group names? In my grid, I have 10 data points divided into 3 groups. When I click on the last row, it returns a count of 12. The code I am currently using is shown below: ...

How can I place an Object in front of an Array in JavaScript?

Currently, I am working on an Angular project where I need to modify a JSON array in order to display it as a tree structure. To achieve this, the objects in the array must be nested within another object. Desired format / output: this.nodes = [ { id ...

Resolving the issues and creating a subtle fade-in effect for the footer positioned at the bottom of the webpage with

I'm attempting to keep my footer fixed at the bottom of the page and have it fade in when scrolling to the very end. However, currently, it remains at the top of the page. Despite trying the code below, I can't seem to get it to display with a " ...

Creating an object for an HTTP POST request in an Angular app by utilizing the values of another object

Within my Angular application, I am working with a Job object and an Offer object. These are the interfaces I have defined: export interface IJob { id: number; title: string; description: string; employeeId?: number; managerId: string; imageU ...

OBJ Raycasting in Three.js

Greetings! I encountered an issue while working with three.js in my project. Specifically, I was trying to select a custom mesh that I loaded from an OBJ file. To troubleshoot, I set up a simple raycaster, a cube, and my custom model (which is also a cube ...

Typescript's way of mocking fetch for testing purposes

I have a query regarding the following code snippet: import useCountry from './useCountry'; import { renderHook } from '@testing-library/react-hooks'; import { enableFetchMocks } from 'jest-fetch-mock'; enableFetchMocks(); i ...

Property does not exist when dispatching in React Redux within componentDidMount

Currently, I am navigating my way through my initial project using React + Redux and have hit a few roadblocks while attempting to dispatch a function in the componentDidMount section. I tried to emulate the Reddit API example project from the Redux docume ...

Testing React components by writing unit tests to locate specific elements within the component

Seeking assistance for a challenge I'm facing with writing unit tests in React. I'm trying to verify the existence of an action button and link button within a component. Below is the code snippet for the component, which renders a child componen ...

Comparison between two identical values results in false

Having a dilemma while comparing two _id values in my MongoDB. Despite using the logical operator ===, it's showing false even though both values are identical. Any thoughts on what could be causing this discrepancy? Any insights would be greatly app ...

Issue with AMCharts: DateAxis on my XY graph unexpectedly zooms out to the year 1970 when stacked

Currently, I am attempting to display multiple processes throughout a single day on an AMChart XY axis. Everything seems to be functioning correctly initially, but as soon as I stack the data, the timeline unexpectedly zooms out all the way to 1970. Howev ...

Is there a more efficient approach to extracting the border width using javascript?

I implemented the following code: const playGard = document.getElementsByClassName("playGard")[0]; const borderW = getComputedStyle(playGard,null).getPropertyValue('border-left-width').substr(0,2); The result I obtained was "10". Is there a m ...

The narrowing of Union types in Typescript is not possible when it is based on

I'm facing an issue with narrowing down a union type in typescript. Let's say we have two interfaces and a union: interface A { flag: true callback: (arg: string) => void } interface B { flag?: false callback: (arg: number) = ...