Ways to achieve the organization of nested promises in Angular 9

I am facing an issue with the order of asynchronous calls in my Angular script. Despite having multiple nested async calls, the order of execution seems to be disrupted.

Here is a snippet of my Angular code:

saveArticles(articles, action) {
    articles.forEach(element => {
      let blobs = [];
      if (element['files'] ) {
        let files: any[] = JSON.parse(element['files']);
        for (let k = 0; k < files.length; k++) {
         //async service 
          this.filesService
            .getBinaryURI(files[k].url)
            .then(blob => {
              let blobElement = {
                fileName: files[k].name,
                fileType: files[k].type,
                blob: blob,
              };
              blobs.push(blobElement);
            })
            .finally(() => {
              if (k == files.length - 1 ) {
                //async service 
                this.articleService.createArticle(element, this.artId, blobs);
              }
            });
        }
      } 
    });
  }
//...
saveArticles(articles, "edit");
saveArticles(articles, "create");

I would greatly appreciate any assistance or guidance regarding this issue. Thank you!

Answer №1

When dealing with asynchronous callbacks, it's important to note that they typically have an undefined ordering, although some exceptions like setTimeout exist. The original calls themselves maintain order within their own execution context. While individual promise chaining can ensure the sequence of each callback, it's often advantageous to structure callbacks in a way where order is not critical and group asynchronous operations logically.

By arranging the callbacks to be order-agnostic, asynchronous operations can run simultaneously without a sequential serialization step introduced by chaining. Higher constructs such as "wait all" can also achieve parallel operation where the order of callbacks is independent.

For instance, instead of relying on k == files.length - 1, employing a simple counter initialized to the files' length and decrementing it ensures the last callback remains the final one regardless of k's value. Additionally, inserting into blobs[k] rather than pushing maintains the blob's order relative to the loop's iteration.

Utilizing Promise.all, handling success through the finally block of a new promise amalgamates the resolved input promises' results without side effects to blobs. This emphasizes using the loop index for proper order management.


To further illustrate, if there is no dependency between two calls to saveArticles, restructuring the code to prioritize stability over callback order ambiguity enables streamlined execution unaffected by deviations in callback sequencing.

let files: any[] = JSON.parse(element['files']);
let blobs = new Array(files.length);
let remaining = files.length;

for (let k = 0; k < files.length; k++) {
  this.filesService
    .getBinaryURI(files[k].url)
    .then(blob => {
      let blobElement = {
        fileName: files[k].name,
        fileType: files[k].type,
        blob: blob,
      };
      blobs[k] = blobElement;
    })
    .finally(() => {
      remaining -= 1;
      if (remaining == 0) {
        this.articleService.createArticle(element, this.artId, blobs);
      }
    });
}

An alternative approach involves leveraging Promise return values and chaining for enhanced flexibility, reduced complexity, and improved organization among saveArticle calls without enforcing strict dependencies.

let files: any[] = JSON.parse(element['files']);
let reqs = [];

for (let k = 0; k < files.length; k++) {
  var reqPromise = this.filesService
    .getBinaryURI(files[k].url)
    .then(blob => {
      let blobElement = {
        fileName: files[k].name,
        fileType: files[k].type,
        blob: blob,
      };
      return blobElement;
    });

  reqs.push(reqPromise);
}

let onAll = Promise.all(reqs).then((blobs) => {
  return this.articleService.createArticle(element, this.artId, blobs);
});

The beauty of the latter method lies in the ability for saveArticles calls to return the 'all promise', enabling superior composition control and ensuring correct synchronization when waiting for completion.

return onAll;

await saveArticles(articles, "edit");   
await saveArticles(articles, "create"); 

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

Obtaining the NativeElement of a component in Angular 7 Jasmine unit tests

Within the HTML of my main component, there is a my-grid component present. Main.component.html: <my-grid id="myDataGrid" [config]="gridOptions" </my-grid> In main.component.specs.ts, how can I access the NativeElement of my-grid? Cu ...

Passing data to a redirected route in Angular using the redirectTo parameter

Is there a way to send the "data" to the home component only when redirected from an old path, and not from an empty path? const routes: Routes = [ {path : '', redirectTo:'home'}, {path : 'oldPath', redirectTo:&apo ...

React modal not closing when clicking outside the modal in Bootstrap

I recently utilized a react-bootstrap modal to display notifications in my React project. While the modal functions correctly, I encountered an issue where it would not close when clicking outside of the modal. Here is the code for the modal: import Reac ...

Is there a way for me to directly download the PDF from the API using Angular?

I'm trying to download a PDF from an API using Angular. Here's the service I've created to make the API call: getPDF(id:any) { return this.http.get( `url?=${id}`, { responseType: 'blob' as 'json', obs ...

Creating a fake Angular2-toaster component for Jasmine Unit testing

Seeking advice: How can I simulate an external Angular2 library for testing purposes? Currently, my application utilizes Angular2-toaster to display popup messages. While attempting to write a unit test suite using Jasmine for one of the components, I tri ...

Deleting a page reference from the page history stack in Angular 4

I am working on my angular web application and I am looking for a way to remove a specific page reference from the angular page history stack. For example, if I navigate from the login page to the dashboard page, I want to remove the login page reference f ...

What responsibilities do the TypeScript configuration files assume once a Vue project has been created?

As a newcomer to Vue.js, I am curious about the function of the typescript configuration files that are created after generating a Vue project. Upon running the npm create vue@latest command and opting for the "Add Typescript?" and "Add Vue ...

Error: Trying to access a property that does not exist (postgresClient) on an undefined variable

For my latest project, I've been working on creating an API that utilizes PostgreSQL to store shortened links. However, I keep encountering the issue where postgreClient is consistently undefined when attempting to run a query to the database. In my ...

Using Angular to automatically update the user interface by reflecting changes made in the child component back to the parent component

Within Angular 5, I am utilizing an *IF-else statement to determine if the authorization value is true. If it is true, then template 2 should be rendered; if false, then template 1 should be rendered. Below is the code snippet: <div *ngIf="authorized; ...

Error in Vue & TS: Property does not exist

Currently working with Vue.js and TypeScript, I've managed to create a DatePicker. However, I'm facing issues when trying to build the project due to TypeScript errors. Below is a snippet from my DatePicker TypeScript file: import Vue from " ...

Using conditional statements to render content based on a certain condition within a

One of my requirements is to dynamically render a React component in the following manner: parent.ts ... <Parent> <Child/> <Parent> ... child.ts ... return (someBoolean && <Component/>) ... While ...

Angular's custom error handler is failing to function as intended

Currently, I am facing a challenge in implementing a custom error handler for my angular application. The issue seems to be working, but under some peculiar conditions only. To view the implementation, you can check out the StackBlitz link provided below: ...

The element is implicitly imparted with an 'any' type due to the incapability of utilizing an expression of type 'number' to index the type '{}'. This error occurs in the context of VUEJS

I have encountered an issue that I have been struggling to resolve despite trying numerous solutions. The problem arises while working on a project using Vue. Here is how I have structured my data: data(){ return{ nodes: {}, edges:{}, ...

Angular2: Determining which checkboxes have been selected

Currently, I am utilizing angular2 and have the following HTML code: <div *ngFor="let val of channelForTabs; let i=index"> <label for="isCheckBox" style="margin-left:15px;">Draw</label> <input id="checkBox{{i}} ...

TypeScript versions 2.3 and 2.4 experiencing issues with generic overloads

After upgrading from typescript 2.2, I encountered an issue with the following example. interface ILayoutResult { id: string; data: any; } interface ILayout{ getResult<T extends ILayoutResult | ILayoutResult[] | void>() :T; } class te ...

The error message related to TupleUnion in TypeScript is indicating that the depth of type instantiation may be too deep and could

Recently, I've been delving into a TypeScript utility type known as TupleUnion. This useful type came to my attention through a fascinating Twitter post, and I've observed it being utilized in various Stack Overflow solutions. Here's how the ...

Having trouble diagnosing issues in a program that utilizes concurrent async/await functionalities

Insights regarding this program: When pressing F11 slowly through the program, not all executions of ProcessURL() are displayed Pressing F11 quickly reveals more executions of ProcessURL() Adding Thread.Sleep(3000); in ProcessURL causes the MainUI ...

Implementing data waiting strategy in Vue component using TypeScript for rendering

When working with the first component, I encountered a scenario where I needed to open a new page using the router functionality: In Component_1.vue: let route = this.$router.resolve({ name: 'Schedule', params : { id: (this.schedule[0].schedule ...

A Guide to Catching Targeted React Notifications in Sentry using Next.js

In my Next.js application, I have implemented Sentry for error tracking. While I have successfully set up Sentry to capture errors within my try/catch blocks, I am currently struggling with capturing specific errors and warnings at a global level in my sen ...

I have successfully implemented useLazyQuery in a functional component, but now I am looking to integrate it into a class component. Can you provide guidance on how to achieve

Recently, I encountered an issue with my functional component that contains 3 checkboxes and 1 button. I utilized the useLazyQuery hook to ensure that my query was only sent upon clicking the button. However, a major drawback is that my component re-rend ...