0

I think an observable may not be the right thing to use here but I'm not sure what else I could use to provide live updates of a variable's value to another class.

I have this loop in a service class

elements.forEach(element => {
  doStuff();
  this.numberSubject.next(valueFromDoStuff);
})

and a subscription to the observable in my component class

numberSubjectObservable.subscribe(result => {
  this.value = result
}

What I want is to be able to pause the loop until my Angular component is updated with the newest value from the subscription but right now it waits until the end before rendering the final value.

Azuzu
  • 11
  • 1
  • 7
  • https://stackoverflow.com/questions/22386325/waiting-for-ui-re-rendering-to-complete does this answer question? It's similar to what you are asking – wormiii Nov 16 '20 at 16:58
  • No, I don't see the relation to that problem.. – Azuzu Nov 16 '20 at 17:02
  • You can use promises with doStuff, when result cames you call next. – Aloiso Gomes Nov 16 '20 at 17:05
  • @AloisoGomes I'm not sure I follow, I execute doStuff within a promise and put next() in the following .then()? That doesn't appear to make the component update at all – Azuzu Nov 16 '20 at 17:14
  • @Azuzu if you need doStuff result before call next promises will do the job – Aloiso Gomes Nov 16 '20 at 17:18
  • @AloisoGomes that doesn't solve the issue I'm having – Azuzu Nov 16 '20 at 17:23
  • @Azuzu I want help you. But i think we here need more details about what you want to achieve – Aloiso Gomes Nov 16 '20 at 17:44
  • @AloisoGomes Order of operations: perform operations on element in the loop, update the numberSubject with the next(value), receive that value in the component class and render the Angular component with the new value, go back to the loop and repeat the process – Azuzu Nov 16 '20 at 18:00
  • @Azuzu, one more question, re-render a component over each loop operation can result in memory leak. Wait last interation before rerender is not good? – Aloiso Gomes Nov 16 '20 at 18:02
  • @AloisoGomes the component is for a progress bar, I need to show progress – Azuzu Nov 16 '20 at 18:04

2 Answers2

1

Not sure if your doStuff() is sync or async. In case it is sync, you don't see any updates in your progress bar, because the change has happened instantaneously, so that you only see the final value.

In case it is async, first of all you have to forget about forEach, it [does not work with it].

I've prepared a demo for you. The first button triggers synchronous iteration. The second button triggers an async iteration.

Faking an async doStuff:

private async asyncDoStuff(element: number): Promise<number>{
  console.log('running async doStuff with element: ' + element);
  return new Promise(resolve => {
   setTimeout(resolve, 1000);
  }).then(()=>{return this.doStuff(element);});
}

And looping through your array to see the updates with a 1 sec delay:

public async asyncIterate(){
  let newElement = 0;
  for (const element of this.elements){
  newElement = await this.asyncDoStuff(element);
  console.log('newElement: ' + newElement);
  this.numberAsyncSubject.next(newElement);
  }
}
Maria K.
  • 219
  • 1
  • 8
  • In my case doStuff() is sync, is there a way to synchronously send progress values as it goes through the loop and ditch the observable altogether? In my case I cannot use a setter function for the component class because that will introduce a circular dependency – Azuzu Nov 16 '20 at 18:38
  • Is this of help? https://medium.com/front-end-weekly/easy-custom-progress-bar-in-angular-cb2670bb2d02 – Maria K. Nov 16 '20 at 18:41
  • Otherwise you can use Angular Material https://material.angular.io/components/progress-bar/overview or a npm package: https://www.npmjs.com/package/angular-progress-bar – Maria K. Nov 16 '20 at 18:42
  • I am using Angular Material's progress bar but the value to populate it comes from a separate service which is the reason I'm having trouble with it. However your code does seem to do what I need it to do, just need to tweak it a bit on my end to hopefully make it perform faster. Thanks! – Azuzu Nov 16 '20 at 18:59
0

Not sure I understand exactly what you are trying to do here but if you need to wait on this.value to equal the numberSubjectObservable result you can use an async method and try the following

you can change this

numberSubjectObservable.subscribe(result => {
  this.value = result
}

to this

this.value = await numberSubjectObservable.toPromise();
CouchCode
  • 180
  • 8