Sunday, March 5, 2023

What's wrong with eager evaluation? (And why you should use fp-ts/Fluture for that)

Eager evaluation in JavaScript Promises can lead to several problems. Eager evaluation means that a Promise's executor function is executed immediately when the Promise is created, rather than when it is needed later on. Here are some potential issues with eager evaluation:

Increased resource usage: If the Promise's executor function performs a resource-intensive operation, eager evaluation can cause unnecessary resource usage. For example, if the Promise is used to fetch data from a server, eager evaluation would mean that the fetch operation is performed immediately, even if the data is not needed until later.

  • Unnecessary blocking: If the Promise's executor function performs a blocking operation (such as a long-running loop), eager evaluation can cause unnecessary blocking of the main thread. This can lead to unresponsive user interfaces and other performance issues.
  • Wasted work: If the Promise's executor function performs work that is not needed (for example, if it fetches data that is never used), eager evaluation can result in wasted work and unnecessary network traffic.
  • Race conditions: Eager evaluation can also lead to race conditions, where multiple Promises are created but only one of them is needed. This can result in unnecessary resource usage and can make code harder to reason about.

To avoid these problems, it's generally better to use lazy evaluation with Promises. In lazy evaluation, the Promise's executor function is only executed when the Promise is needed (for example, when it is passed to a then() method or when it is awaited in an async/await function). This approach allows for more efficient use of resources and can help prevent performance issues.

The fp-ts library provides several abstractions and functions that can help avoid the problems associated with eager evaluation in Promises. For example, the type Task is essentially a Promise wrapped in a function, allowing you to control when it's executed, therefore it's considered "lazy".

import { task } from 'fp-ts/lib/Task'

const fetchTask = task(() => fetch('https://example.com/data'))

If you call fetchTask(), only then will your HTTP call be executed.

Fluture

There are also other viable options, like Fluture, which is a Future implementation in Javascript. I'm not aiming to discuss Future-s here, but it might be mentioned, that it is a monadic interface that is lazy by it's nature - in a way it's similar to Promise, but more functional, with it's advantages.

No comments:

Post a Comment