Use cacheResult
of ImmutableJs Seq
ImmmutableJs's Seq is a streaming data source, i.e., it does not store the intermediary results. Unlike traditional Arrays, an operation such as a map
does not process all elements immediately. It is lazy, as it does only the minimal amount of work to respond to a function call.
It is usually a good thing, as it makes some use cases possible traditional collections are not suited for.
But it can backfire in some cases.
Let's say we have lots of users, but only a fraction of them are active. There are also messages for the users, and we'd like to know how many messages are for the active members. Since there are a lot of users, instead of storing them all in memory, we stream it using a Seq.
First, filter by activeness:
const activeUsers = users.filter(({active}) => active);
Then filter and count the messages that are for the active users:
messages
.filter(({userid}) => activeUsers.some(({id}) => id === userid))
.count();
It works, but it takes O(users * messages)
time . For lots of users and messages, this can easily be a performance bottleneck.
Use cacheResult()
The problem is that activeUsers
iterate over the users
Seq for every single message. Since there are a lot of users but much fewer active ones, the critical operation is the iteration over the users
. In this case, it makes sense to forgo streaming processing and store the active users separately.
To force this behavior, use cacheResult()
. It evaluates the Seq and stores the intermediary result.
const activeUsers = users
.filter(({active}) => active)
.cacheResult(); // Here's the magic
With this one-line change, the time needed to calculate the result is reduced to O(active users * messages)
.
Don't overuse it
For some cases, it's a silver bullet. But use it strategically. Were most of the users to be active, forcing evaluation would introduce a memory bottleneck.
In some cases, streaming processing is the way to go, in others, evaluated ones.