When I have multiple observers subscribed to the same observable, calling the remove() function on one of them causes the others to stop receiving notifications as well.
It seems that the remove() function in an observer removes all observers from the observable, not just the one that called it.
But in this case Iâm calling the remove() function on a specific observer. As I understand it, that should only remove that particular observer and can be an alternative to using the observableâs remove function, as noted in the documentation.
Removing an Observer modifies the array with all Observers from an Observable.
The array is only a reference, and any modifications during the loop execution will affect the next iteration on the same loop. Adds on top of it that the observers are executed in sequence, not in parallel.
Get the following example:
const arr = [0 , 1, 2, 3];
for (const val of arr) {
console.log(val);
arr.shift(); // Removes the first position of the array
}
The result is:
0
2
This is due of the usage of a pointer to get the next position on the array and modifying the array during the loop.
In your example, if you had a third observer, the third one would be executed.
Thus, at this moment you cannot remove the handlers inside of the handler itself, which can be considered a bug, or just âworking as intendedâ.
Nevertheless, if you want to execute only once, you can use Observable.addOnce() instead of Observable.add(). If that is not the cause, you have to find another way to remove the handlers outside the loop of execution.
Thanks, I understand the issue now. However, Iâve noticed that if I call testObservable.remove(observer1) instead of calling remove() from within the observer itself, the issue doesnât occur: all observers are executed as expected.
Given that, wouldnât it make sense for both approaches (observer.remove() and observable.remove(observer)) to manage the observers in the same way internally, so that they behave consistently? Or is there a specific reason why theyâre handled differently?
I think the solution is to call the public remove function instead of the private one when attaching it to the observer to avoid modifying the observers array during iteration.
Observer.remove() has a different behaviour than Observable.remove(observer).
Basically, Observer.remove() doesnât defer the removal of the observer, and it affects the array that is being iterated inside Obversable.notifyObservers(), like showed on my comment here.
Oh I see! Ok yeah we can totally use the public remove then instead of _.
BUT that will trigger a large breaking change so we may want to have an option to have it off by default but let the user choose to do it (like Observer.remove(TRUE))
sorry to bump.. but wouldnât it be better to use a reverse loop in this case?
aka
for (let i = this._observers.length - 1; i > -1; i--) {
const obs = this._observers[i];
then thereâs no need for timeouts, multiple different remove / unregister functions etc
unless iâm missing something obvious
Edit; i guess order in which the observers are called compared to when they are registered would change
But that can be sorted âbehind the sceneâ by always inserting first and making insertFirst? push to the end of the array instead, so it still means âexecute firstâ
I guess even with the âinserFirstâ adjustment this could prod unexpected side effects (especially when interacting with the array directly, even if thatâs not recommended). So it might be considered a significant breaking change.