2

I am trying to do task step by step.

I have a for loop in a method:

async changeTimeStep3() {
  for (let i = 1; i < 10; i++) {
    await this.do(i)
  }
}

for each step must do() task.

do(i) {
  this.http
    .getDataFromServer(
      "api/example?id=" +i
    )
    .subscribe((response) => {
     console.log(i);
    });
}

  

I want to wait to get response and after response coming go to next i

But not work console.log print:

2
3
5
1
4
7
8
9
6

Note time to receive response from api is not fix.

Any help?

4
  • 1
    do doesn't return a promise to await... Commented Dec 13, 2021 at 13:51
  • why? and how to do in right way? @jonrsharpe Commented Dec 13, 2021 at 13:54
  • I don't know why it doesn't return a promise, you presumably wrote it like that, or why you're awaiting it even though it doesn't. And if you want to await it, try actually returning a promise that resolves when the work is done (or rejects if it fails). Commented Dec 13, 2021 at 13:57
  • I realize that this doesn't answer your question, but making calls like that is an anti-pattern. If you need to await multiple calls in a loop, you need to pass a collection to the query. Commented Dec 13, 2021 at 13:57

3 Answers 3

8

To get your loop print out the values in the correct order you could transform your observables into promises, so your await will block the for-loop as long as the HTTP call hasn't resolved.

import { firstValueFrom } from 'rxjs';

...

async do(i: number) {
  await firstValueFrom(
    this.http
      .getDataFromServer(
        "api/example?id=" +i
      )
  );
  console.log(i);
}

Using firstValueFrom or lastValueFrom are the RxJS ways to get a promise as return value from an observable (since toPromise got deprecated, you can read more about that here).

Sign up to request clarification or add additional context in comments.

Comments

3

You can return a Promise and use resolve() in response part of your ajax. Like:

do(i) {
  return new Promise((resolve, reject) => {
    this.http.getDataFromServer("api/example?id=" +i).subscribe((response) => {
      resolve(response);
    }, (error) => {
      console.error(error);
      reject();
    });
  });
}

12 Comments

I was editing. yes it is parallel. I put the sequential model too. @jonrsharpe
loops in for (let i = 0; i < x; i++) {} are different from for (const i of arr) {} model. @jonrsharpe
and just now I came up with a better answer...
work like a sharme, thank you !!
This clumsy new Promise wrapping an Observable really isn't the way to go. I don't know why OP marked it as accepted. @Batajus's answer above is far better.
|
2

The problem here is that do is not an asynchronous method; it is a synchronous method that triggers an asynchronous one. As such, the await completes immediately.

You may be able to do this:

do(i) {
  return firstValueFrom(this.http
    .getDataFromServer(
      "api/example?id=" +i
    ))
}

It converts the observable into a promise and returns it. So, now the do() method returns an async object. To output from the do method; you can use a pipe:

  return firstValueFrom(this.http
    .getDataFromServer(
      "api/example?id=" +i
    ).pipe(tap((result) => {console.log(i);}))

All that said, using async/await in Angular is unusual. An RXJS operator is usually considered the right approach. Possibly with concat

4 Comments

Subscribing returns a subscription, not a promise - that has the same problem the OP currently does.
I think this has a completely different problem, but good call on the promise/subscribe issue.
It has the same problem insofar as you're awaiting something that's not a promise, therefore awaiting it is pointless because it doesn't synchronise the behaviour.
@jonrsharpe I edited the answer; so it should be more accurate now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.