0

I'm trying to execute a function after a success response of previous function has returned. Have been trying, with different approaches, but still in vain.

I want my service to post newLoan only after the the loanTerms have been added (which is a API call), but doesn't waits for that an execute the next function without the response of previous.

Before posting this question, I already tried different methods, Even though I'm putting my code inside the subscribe method of function. but still I doesn't performs the way I want it. The problem is that I've list of products, and I've to perform a network operation on each product and then execute my other function, but it only waits for just 1st product, after that I doesn't wait and executes the next function. Here is my code

{
    this.newLoanForm.value.invoiceDate = this.loanDate.format();
    document.getElementById('submitButton').style.display = 'none';

   
    // Adding number of months against give loan term ID
    let loanProducts = this.loanProductForm.value.products;
    let loanTerm;

    loanProducts.forEach(product => {
      this.loanTermService.getLoanTerm(product.loanTermId).subscribe((response: any) => {
        // console.log('Number of months: ', response.numberOfMonths)
        loanTerm = response.numberOfMonths;
        product.installmentStartDate = this.installmentStartDate.format();
        product.monthlyInstallment = product.total / loanTerm;

        // I want this function to executed after all the products have been completed their network activity, but it only waits for just 1st product, after that it executes the below code. how do I make it wait for all products.
        this.loanService.postLoan(this.newLoanForm.value).subscribe((response: any) => {

          console.log('Loan added successfully: ', response);
          PNotify.success({
            title: 'Loan added Successfully',
            text: 'Redirecting to list page',
            minHeight: '75px'
          })
          document.getElementById('submitButton').style.display = 'initial';
          this.router.navigate(['searchLoan']);

        }, (error) => {
          console.log('Error occured while adding loan: ', error);
          PNotify.error({
            title: 'Error occured while adding loan',
            text: 'Failed to add new loan',
            minHeight: '75px'
          })
          document.getElementById('submitButton').style.display = 'initial';
        })

      }, error => {
        console.log('Error while retrieving loanTermId: ', error);
      });
    });


    this.newLoanForm.value.loanProducts = loanProducts;
    console.log('Loan Products: ', this.loanProductForm.value);

here's how I tried the above code with promise and async and await

async calculateInstallments() {
    // Adding number of months against give loan term ID
    this.loanProducts = this.loanProductForm.value.products;
    // let loanTerm;

    this.loanProducts.forEach(async product => {
      console.log('Call to get loanTerms: ', await this.loanTermService.getLoanTermById(product.loanTermId));
      let response: any = await this.loanTermService.getLoanTermById(product.loanTermId);
      await this.loanProductService.getLoanProductByLoanId(product.loanTermId).then(() => {
        let loanTerm = response.numberOfMonths;
        console.log('loanTerms', loanTerm);
        product.installmentStartDate = this.installmentStartDate.format();
        product.monthlyInstallment = product.total / loanTerm;
      });

    });
  }
// putting the function I want to execute after the response of previous in the `then` method

    await this.calculateInstallments().then(() => {

      this.newLoanForm.value.loanProducts = this.loanProducts;
      // Posting loan after the response of loanTerms Service
      this.loanService.postLoan(this.newLoanForm.value).subscribe((response: any) => {

        console.log('Loan added successfully: ', response);
        PNotify.success({
          title: 'Loan added Successfully',
          text: 'Redirecting to list page',
          minHeight: '75px'
        });
        document.getElementById('submitButton').style.display = 'initial';
        this.router.navigate(['searchLoan']);

      }, (error) => {
        console.log('Error occured while adding loan: ', error);
        PNotify.error({
          title: 'Error occured while adding loan',
          text: 'Failed to add new loan',
          minHeight: '75px'
        });
        document.getElementById('submitButton').style.display = 'initial';
      });


    });

but it didn't work unfortunately.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Lint
  • 895
  • 3
  • 13
  • 32
  • Does this answer your question? [Callback after all asynchronous forEach callbacks are completed](https://stackoverflow.com/questions/18983138/callback-after-all-asynchronous-foreach-callbacks-are-completed) – Klaycon Dec 31 '19 at 20:20
  • I already tried promises, but it didn't worked – Lint Dec 31 '19 at 20:21
  • 1
    your `async/await` snippet makes it very clear that you didn't read the question I linked above. You **can't** use forEach like that to make the async function wait until each product has been processed. Use a normal loop. – Klaycon Dec 31 '19 at 20:27
  • will normal loop work fine in this scenario ? – Lint Dec 31 '19 at 20:31
  • `.forEach()` calls an async function immediately on each element of the array without waiting for the previous to finish. if you use a regular `for` loop, you can `await` the processing of each element before the loop continues to the next iteration – Klaycon Dec 31 '19 at 20:33
  • I think your first example could benefit from utilizing `forkJoin` from `rxjs`. You make an array of Observables and then subscribe to the execution of the entire array. If all of your requests succeed, the subscribe is executed. If any of them fail, the whole thing fails and you can catch the failure instead. Related/some examples: https://stackoverflow.com/questions/48300456/forkjoin-issue-on-angular – WGriffing Dec 31 '19 at 20:44
  • @Klaycon I tried but its not working either :( – Lint Dec 31 '19 at 22:46

1 Answers1

1

I just answered a question today, about almost the same problem. Maybe you still need a solution, otherwise see it as another way to go.

Doesn't mind if you use async await or daisy chain style with new Promise. Since version 3.x of async framework, you'll be able to use the amazing iteration functions (don't know if all) as promise, if you don't use callback.

This is an simple example, how you could use the eachOf function for asynchronously tasks.

const async = require('async');

let items = [
    { firstName: 'John', lastName: 'Doe' },
    { firstName: 'Jane', lastName: 'Doe' },
    { firstName: 'Me', lastName: 'Myself And I' }
];

async.eachOf(items, (item, index, callback) => {

    //here you could query db with vaulues from items array as item
    console.log('this is item:', item);

    new Promise(resolve => {
        setTimeout(() => {
            resolve(true);
        }, 500);
    })
    .then(result => {
       //maybe you need to do something else
       console.log('this is the result:', result);

       callback();
    });
})
.then(() => {
    //working ahead with daisy chain
    console.log('All items updated');
});

I hope you can work with this setup or it's an inspiration to restructure this and using async await in another handy way.

Maximilian Fixl
  • 670
  • 6
  • 23