Returning A Promise From A JavaScript Function Is Useful
Josh Pollock
Posted on January 7, 2020
One JavaScript feature that has tripped me up a bit recently is the difference between a function that returns a promise and a function that returns the value of that the promise resolves. Decoupling data fetching helps with server-side rendered apps and can also make mocking a remote API in tests easier. In my case, I wanted to use the same functions for getting data into a static file generator as I used in the React client.
In the past, I always wanted to return the result of the API call. The more that I understand the subtle difference between returning a promise and returning the result of the the promise, the more I prefer to return a promise. In this post, I will show to return a promise from an asynchronous function, with and without explicitly creating a Promise object with the new keyword. More importantly, I'll cover when and why this pattern is useful when working with remote APIs.
This is a quick post and I assume you are somewhat familiar with using async and await in JavaScript. I wrote in more detail about that here.
What Is A Promise?
If you are not familiar with promises, I recommend reading MDN first. A useful definition for this article, would be to say it's a function that promises to do something and then let's you do something with it.
The kinds of functions we're talking about are considered "thenable". We are able to call the function "then()" on there results.
Fetch is an example of a "thenable". This example makes a remote HTTP request and then console.logs the response.
fetch(url).then(r => console.log(r) )
Returning A Promise From A Function
There are a few ways to return a promise from a function. One way is to use an async closure that returns a thenable function. This example gets a page of posts from a WordPress site:
function async fetchPosts(page = 1){
// Await fetch of posts
const posts = await fetch( `https://site.com/wp-json/wp/v2/posts?page=${page}` )
// Return posts
return posts;
}
We can add a promise chain in this closure for repetitive logic. For example, parseing the response from JSON to an object:
function async fetchPosts(page = 1){
// Get posts
const posts = await fetch( `https://site.com/wp-json/wp/v2/posts?page=${page}` )
//Then parse
.then( r => r.json() );
// Then return object
return posts;
}
This example is probably more useful then the first one. This pattern is useful when we want to consume this function in a closure that can not be async. For example:
React.useEffect( () => {
//Fetch posts, THEN update state
fetchPosts(page).then(posts => setPosts(posts) );
},[page]);
Keep in mind, this is a function that returns a promise to make an API request. Not a function that makes an API request. That means that calling then()
triggers the request. Another way to do it is to use Promise.all()
//Get page 1 and page 2 of posts:
Promise.all([ fetchPosts(1), fetchPosts(2) ] ).then( posts => {
const page1 = posts[0];
const page2 = posts[1];
});
Using The Promise Constructor Inside An Async Closure
The other way to return a promise from a function is to instantiate a promise object, and return it. This lets us manually call the resolve and reject functions.
We can use this to add a caching layer to our fetchPosts()
function. The function can resolve immediately, if the page of posts is in the cache:
let cache = {};
function async fetchPosts(page = 1){
return new Promise( asnyc (resolve, reject) => {
//Is post in cache?
if (cache.hasOwnProperty( page) ){
//Resolve from cache right away
resolve( cache.page );
}
//Make request
const posts = await fetch( `https://site.com/wp-json/wp/v2/posts?page=${page}` ).then( r => r.json() );
//cache for next call.
cache[ page ] = posts;
//now resolve
resolve( posts );
});
});
I Promise This Is Useful
In this post, I showed how to return a promise from an asynchronous JavaScript function.
I find this to be a useful pattern when abstracting API calls from UI or other business logic. What I like is that it leaves the responsibility of when to call the function to the module consuming it.
Featured Image: Photo by Agnes Gospodinova on Unsplash
Posted on January 7, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.