Today, I spent a good chunk of my workday debugging an issue I had with an observable stream spawned from the combineLatest function. I want to share this life lesson in hopes others do not suffer a similar fate.
I've boiled the problem down to the code snippet below:
// Will output one value, an array of 3 numbers
const oneValue$ = of([10, 20, 30]);
// Will output two values
const twoValues$ = of(100, 200);
combineLatest(oneValue$, twoValues$).pipe(
map(([val1, val2]) => {
return {
a: val1.splice(0, 1)[0],
b: val2
}
})
).subscribe(output => console.log(output));
// Desired Output:
// { a: 10, b: 100 }
// { a: 10, b: 200 }
// Actual Output:
// { a: 10, b: 100 }
// { a: 20, b: 200 }
Notice the desired vs. actual outputs. Let's walk through this example:
We have two observable streams, one that will emit only 1 value in its lifetime, the other will emit two values.
Using combineLatest I want to construct a new return value based on the latest values from each stream.
I map the two emitted values [val1, val2]
to a new object.
I want this new object to have two properties where
- property
a
is the first value in the array fromoneValue$
(i.e. the value 10) - property
b
is the latest value intwoValues$
(i.e. the value 100, then 200)
So I want my output to look like
// Desired
{ a: 10, b: 100 }
{ a: 10, b: 200 }
// Actual
{ a: 10, b: 100 }
{ a: 20, b: 200 }
My a
value isn't always the first value from the array. Strange?
combineLatest should re-emit the last value from oneValue$
when twoValues$
emits the second time. That should always be the array [10, 20, 30]
. I'm using the function splice(0, 1)[0]
to grab that first item in the array, yet the second emission seems to have grabbed the second item of the array.
Have you figured it out yet?
The splice()
function is permanently altering the array values
combineLatest is emitting the same array value during the second emission, but
The splice()
function actually alters the values of the array during each mapping. Remember, JavaScript is always passing by reference. In our map()
function, val1
is another variable that is pointing to the same array object being held in the combineLatest operator. We then modify the array contents. So when combineLatest passes the array reference to map()
the second time, we receive the same (modified) array reference.
Obvious fixes are to use the spread
operator if it's an array or object of primitive values, or possibly pulling in something like cloneDeep
from the lodash
library for more complex objects.
Many array functions return new arrays without altering the original, however, not all do. Make sure you make a conscious decision around this!
Top comments (3)
val1.toSpliced(1)[0]
developer.mozilla.org/en-US/docs/W...
Almost 2 years later I want to say THANK YOU for your article, I was facing the same issue today and killed several hours trying to debug it. If it weren't for your article I would probably spend days tracking it down.
Thank you, that was driving me crazy! 🤯