Reputation: 145
I have a Service that calculates the solution to a given Sudoku (implementation unimportant) and publishes each iteration of the calculation in a RxJS Observable.
solve() : Observable<Field[][]> {
let observable = Observable.create((o: Observer<Field[][]>)=> {
let iterationCount : number = 0;
while (!this.sudokuGame.isFinished() && iterationCount < 500) {
iterationCount++;
this.eliminateOptions();
o.next(this.sudokuGame.boardFields);
}
if(this.sudokuGame.isFinished())
o.complete();
else
o.error("The game could not be finished after 500 iterations.");
});
return observable;
}
I do this so that I can later use Observable.zip()
to combine this observable with a Observable.interval()
Observer to display the iterations this solving-process goes through with a delay noticeable by humans in my Angular2-component.
However, when I set the data emitted by the Observable in my Angular2-component like so:
Observable.zip(this.sudokuSolverService.solve(),Observable.interval(500),(obs, timer) => {return obs}).subscribe((nextBoardFields: Field[][]) => {
this.sudokuGame.boardFields = nextBoardFields;
});
Then the emissions of the Observable are delayed, but the changes show up instantly nonetheless. I suspect this being caused by me copying the nextBoardFields
-Array by reference, however I am unaware of any methods to unlink the boardFields
-Array in my Angular2-controller from the boardFields
-Array in my sudoku-solving service.
Is there any possibility of doing this?
Edit: The only thing that comes to my mind is to somehow make the Field
class immutable (perhaps with the help of Immutable.js), which seems a bit complicated for this matter.
Upvotes: 3
Views: 1803
Reputation: 145
Maarek's answer pushed me in the right direction.
The key was that even if I copied the Field[][]
-Array with methods such as [].concat(nextBoardFields)
or Maarek's Object.assign(this.sudokuGame.boardFields, nextBoardFields)
, the individual Field
's would still refer to the same objects.
To deep copy this array, I made use of o.next(JSON.parse(JSON.stringify(this.sudokuGame.boardFields)))
which allowed me to use my Observable as intended.
Upvotes: 0
Reputation: 18665
I guess the problem here with your solve
observable is that it emits its values synchronously and in one go. So in the same tick you have all the onNext
called, and also the onComplete
. So when you zip
, it gets the first value, waits for the timer, but in the meanwhile, your observable has emitted all its values on that same tick. What you probably need to do is to have your iteration variable linked to the timer, such as :
Observable.interval(500).scan(function(_, iterationCount){
// your logic here
return {
iterationCount : iterationCount
isFinished : // your logic here
boardFields : // your logic here
}
}, {})
.doWhile(function(x){return x.iteration < 500 && !x.isFinished})
.map(function(x){return x.boardFields})
Upvotes: 2
Reputation: 596
I've used Object.assign() to get a by value copy of an object and break the connection to the observable/observer. Would that work in your case?
Observable.zip(this.sudokuSolverService.solve(),Observable.interval(500),(obs, timer) => {return obs}).subscribe((nextBoardFields: Field[][]) => {
Object.assign(this.sudokuGame.boardFields, nextBoardFields);
});
Upvotes: 0