Last active
August 22, 2024 02:04
-
-
Save ehpc/2a524b78729ee6b4e8111f89c66d7ff5 to your computer and use it in GitHub Desktop.
How to compose promises with Ramda
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Custom promise-based compose | |
const composeWithPromise = (...args) => | |
R.composeWith((f, val) => { | |
if (val && val.then) { | |
return val.then(f); | |
} | |
if (Array.isArray(val) && val.length && val[0] && val[0].then) { | |
return Promise.all(val).then(f); | |
} | |
return f(val); | |
})(args); | |
/**************************/ | |
/******** EXAMPLE *********/ | |
/**************************/ | |
const getRandomLimit = (min, max) => Math.random() * (max - min) + min; | |
const fetchPokemonList = limit => { | |
return fetch(`https://pokeapi.co/api/v2/pokemon?limit=${limit}`).then(r => | |
r.json() | |
); | |
}; | |
const fetchPokemon = name => { | |
return fetch(`https://pokeapi.co/api/v2/pokemon/${name}/`).then(r => { | |
return r.json(); | |
// Uncomment the line below to see error handling | |
// throw new Error("Simulated error"); | |
}); | |
}; | |
const cleanupPokemonData = data => ({ | |
id: data.id, | |
name: data.name, | |
weight: data.weight | |
}); | |
//////////////////////////////////// | |
// Built-in promise-based compose // | |
//////////////////////////////////// | |
const getPokemons = R.composeP( | |
R.map(cleanupPokemonData), | |
Promise.all.bind(Promise), | |
R.map(fetchPokemon), | |
R.map(x => x.name), | |
R.prop("results"), | |
fetchPokemonList | |
// getRandomLimit --> this doesn't work because it doesn't return Promise | |
); | |
getPokemons(3, 10) | |
.then(r => console.log("Here are your pokemons:", r)) | |
.catch(err => console.log("Catched an error:", err)); | |
//////////////////// | |
// Custom compose // | |
//////////////////// | |
const getPokemonsCustom = composeWithPromise( | |
R.map(cleanupPokemonData), | |
Promise.all.bind(Promise), | |
R.map(fetchPokemon), | |
R.map(x => x.name), | |
R.prop("results"), | |
fetchPokemonList, | |
getRandomLimit | |
); | |
getPokemonsCustom(3, 10) | |
.then(r => console.log("[custom] Here are your pokemons:", r)) | |
.catch(err => console.log("[custom] Catched an error:", err)); |
Great stuff thanks!
I have added some types to your function as well as specific handling for the last call. For the scenario where the return value is an array of promises. In that case, the function does a Promise.all() before returning, which changes the return type from e.g.:
Promise<number>[]
to
Promise<number[]>
It's a choice, but since the same choice is made for intermediate calls, it makes it cleaner.
You can also find it in the package npm swiss-army-knifey
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
🔥 thanks for this!