Let's come back to our API request:
const main = async () => {
const response = await fetch("https://pokeapi.co/api/v2/pokemon/garchomp/");
const json = await response.json();
return json;
};
main().then(console.log);The program can be divided in 2 steps:
- Execute the
fetchrequest - Extract
jsonfrom the response
const fetchRequest = () => fetch("https://pokeapi.co/api/v2/pokemon/garchomp/");
const jsonResponse = (response: Response) => response.json();
const main = async () => {
const response = await fetchRequest(); // 1️⃣
const json = await jsonResponse(response); // 2️⃣
return json;
};
main().then(console.log);Just like with plain typescript, with effect we need to use different APIs when working with async or sync functions.
Therefore, the first thing to consider is that these are both async operations. For those we can use Effect.promise.
Effect.promise is a wrapper around any async operation. It returns an Effect containing the result of calling the async function.
The equivalent to
Effect.promisefor synchronous operations isEffect.sync.
Converting from native typescript to effect only requires wrapping your async operation with Effect.promise:
import { Effect } from "effect";
// 👇 Promise<Response>
const fetchRequest =
() => fetch("https://pokeapi.co/api/v2/pokemon/garchomp/");
/// 👇 Effect<Response>
const fetchRequest = Effect.promise(
() => fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")
);Notice how we are precisely wrapping a Promise inside Effect.promise:
// 👇 Promise<Response>
const promiseRequest = () => fetch("https://pokeapi.co/api/v2/pokemon/garchomp/");
/// 👇 Effect<Response>
const fetchRequest = Effect.promise(promiseRequest);Same for response.json():
import { Effect } from "effect";
/// 👇 Promise<unknown>
const jsonResponse = (response: Response) => response.json();
/// 👇 Effect<unknown>
const jsonResponse = (response: Response) => Effect.promise(
() => response.json()
); We went from
Promise<Response>toEffect<Response>:
Promise<Response>is eager (async operation executed immediately)Effect<Response>is lazy (it requires explicit execution and can be executed as many times as needed)
Understand Effect
What we did since now is wrapping a native API with Effect.promise:
const fetchRequest = Effect.promise(
() => fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")
);
const jsonResponse = (response: Response) => Effect.promise(
() => response.json()
);We said before that effect is a composition of steps:
() => fetch("https://pokeapi.co/api/v2/pokemon/garchomp/")() => response.json()
Based on which API we use, we are telling
Effecthow to execute the wrapped function.
Effect.promiseis saying: "Execute this function asynchronously and return its result"
But how is that any different from what we were doing before? What are the benefits?
In the following lessons we will explore how we can compose effects together. This compositional quality of effects allows to define error handling, timeouts, filtering, and more.
Building an app with effect is all about defining how to handle each step, and then compose them together.
Let's see next how to handle the composition part.
