Originally posted on my personal blog.
Intro (completely off-topic)
It's has been almost 3 months since my last blog post. There are reasons for that.
First, despite all precautions, I got sick with coronavirus (COVID-19) in the second half of June 2020. For two weeks it was total hell. Very bad well-being, I could only lie in bed and hope that it will go away soon. After that, it was a recovery for the next 2-3 weeks. Now I'm finally got back to normal life and even resumed my fitness training. So, coronavirus is no joke. Please, stay safe.
Second, there are lots of things happening right now in my home country - Belarus. Belarussians are fighting against dictatorship. Our (ex)-president lost last elections which were held on August 9th, 2020, but he continues to stay in power using brutal police and army forces against peaceful people and to threaten to anybody who disagrees with him. But we keep on fighting and to protest every day. I do take all these events very close to heart and hope to wake up one day in a free, democratic, and prosperous Belarus.
Now back to the topic.
What is a Promise in Javascript
A Promise is an object representing the eventual completion or failure of an asynchronous operation.
A Promise may be in one of the following states:
- pending
- fulfilled
- rejected
One of the most widely used examples of asynchronous operations in Javascript is a Fetch API. The fetch() method returns a Promise.
Assume that we fetch some data from a backend API. For this blog post, I'll use JSONPlaceholder - a fake REST API. We will fetch a user's data with the id = 1:
fetch("https://jsonplaceholder.typicode.com/users/1")
Let's see how we can access returned data.
1 - .then() chaining
It is the most simple and the most obvious way.
fetch("https://jsonplaceholder.typicode.com/users/1") //1
.then((response) => response.json()) //2
.then((user) => {
console.log(user.address); //3
});
Here we (1) fetch data from the API, (2) transform it into JSON object and then (3) print user's address value to the console.
The result is:
{
street: 'Kulas Light',
suite: 'Apt. 556',
city: 'Gwenborough',
zipcode: '92998-3874',
geo: { lat: '-37.3159', lng: '81.1496' }
}
2 - Use returned value later in a code
But what if we'd like to use the returned value somewhere later in code?
If we try to do it like this (wrong way!):
const address = fetch("https://jsonplaceholder.typicode.com/users/1")
.then((response) => response.json())
.then((user) => {
return user.address;
});
console.log(address);
We'll get
Promise { <pending> }
It's happening because the Javascript code always executes synchronously, so the console.log() function starts immediately after the fetch() request, not waiting until it is resolved. In the moment when console.log() function starting to run, a Promise that should be returned from a fetch() request is in a pending status.
That said we can access the returned value of a Promise object in another .then() callback:
const address = fetch("https://jsonplaceholder.typicode.com/users/1")
.then((response) => response.json())
.then((user) => {
return user.address;
});
const printAddress = () => {
address.then((a) => {
console.log(a);
});
};
printAddress();
OR using async / await syntax:
const address = fetch("https://jsonplaceholder.typicode.com/users/1")
.then((response) => response.json())
.then((user) => {
return user.address;
});
const printAddress = async () => {
const a = await address;
console.log(a);
};
printAddress();
In both ways, we'll get:
{
street: 'Kulas Light',
suite: 'Apt. 556',
city: 'Gwenborough',
zipcode: '92998-3874',
geo: { lat: '-37.3159', lng: '81.1496' }
}
Conclusion
A Promise object is widely used in Javascript async programming. And it's sometimes confusing for developers how to use it properly. In this blog post, I've attempted to describe a use case when a developer needs to use a returned value from a Promise object somewhere later in code.
Top comments (48)
It's really important to note that the
Promise
object doesn't return a value, it resolves a value via thethen()
method.It is the
fetch()
function that returns a value, which is aPromise
instance.It is the
Promise
instance on which you call thethen()
method, passing in a callback function, which will be eventually be fired when the async code finishes (and internally, callsresolve()
).As an example, here's what a custom
load
function might look like:Note that the
Promise
object will resolve any nested promises as part of its work, so resolvingres.json()
which results in aPromise
being created will be resolved internally before the final chained.then(console.log)
is called.The trick to Promises is:
Promise
s you create (or functions that returnPromise
s) so they can be chainedresolve()
orreject()
within the promiseexecutor
so.then()
or.catch()
will be called and the chaining will completeCreated an account just to say thank you for this breakdown.
Well that's very kind of you. I'm glad it helped!
Really COOL and helpful stuff!
It saves me from Promise hell.
I spent hours on Stack Overflow and Google and fixed my need with this great article.
Although I can only learn about Belarus through news and Lev Vygotsky, I hope your country makes it.
Thank you again.
Best wishes from Taiwan.
Really happy that my blog post was helpful to you!
And thanks for your support! We (Belarusians) must win this fight!
Thank for the explanation of code.
...when you win you'll understand that you loose, but too late. from Ukraine with best wishes.
I am another one who created an account to say Thank You to Kate for the article and @davestewart for amplifying it! I've been having this trouble too and wanted to understand what I am doing wrong. Now I seem to understand it.
First of all i guess you are feeling ok,secondly i send my positive thoughts of love peace to your country.Lastly thanks i was in a hell trap .but your articles has really help to sort it out one Love .
Thank you very much! Appreciate your words!
const address = fetch("jsonplaceholder.typicode.com/users/1")
.then((response) => response.json())
.then((user) => {
return user.address;
});
const printAddress = async () => {
const a = await address;
console.log(a);
};
let d = printAddress();
console.log(d); <-------- how do I do this? this still returns a Promise { }
I want to access return value from printAddress()
That's the whole point! You can use the return value from the Promise object ONLY in a async function.
You should do all the stuff that you'd like to do with the return result in a printAddress() async function.
But what if i had to take the return value of Promise to use as data to render ?
my case is that : I have to query database to get the link for 'src' attribute ? I use mongoosejs
Hope you can explain . Thank You
i find a new solution
the best way to use a promise ...... use a promise inside a promise ...it will work
Hi Kate,
Thanks for the article.
I only get results when I write down the code (with my own api endpoint) like this:
fetch("https://api.spoonacular.com/recipes/complexSearch?number=2&apiKey=b4408aa9ab144e47ae2bf8eff93e72f5")
.then((response) => response.json())
.then((user) => {
console.log(user)
});
I don`t understand what you mean by console.log(user.address) or how to adapt this into your own code.
Anyway, from the results:
results: Array(2)
0: {id: 716426, title: 'Cauliflower, Brown Rice, and Vegetable Fried Rice', image: 'spoonacular.com/recipeImages/71642...', imageType: 'jpg'}
1: {id: 715594, title: 'Homemade Garlic and Basil French Fries', image: 'spoonacular.com/recipeImages/71559...', imageType: 'jpg'}
length: 2
I want to make an array with only the recipe numbers, something like this:
recipeArray = [716426, 715594] and use this for a declaration like let defaultIndex = recipeArray[0];- for another piece of code.
Do you know how to do this? I`d be very grateful.
You can try something like that:
const getRecipes = async () => {
fetch("https://api.spoonacular.com/recipes/complexSearch?number=2&apiKey=b4408aa9ab144e47ae2bf8eff93e72f5")
.then((response) => response.json())
.then((user) => {
const recipes = user.results.map(result => result.id)
console.log(recipes)
return recipes
});
}
const recipes = await getRecipes();
You'll get an array of recipe ids.
Hi Kate,
Thank you so much for your time and trouble, I appreciate!
I've tried your code but I get this error because of the 'const recipes = await getRecipes()' line:
Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules
Do you have any suggestions? Api fetch is pretty new to me and my vocabulary with this is limited.
could you please DM me or provide me a link to your code somewhere online (github repo, code sanbox and so on), so I could take a look and try to help you?
Sure, it`s github.com/TomKnn/EindopdrachtMood....
To give you some context, I'm re-educating from illustrator/'art'teacher to front end developer due to lack of work opportunities. I like it very much but the course is online with little help, I'm stuck for months now on little things.
This is my final assignment where we have to a modest site design with an api function.
It's a bit of a mess, but I'll tweak it as soon as my code works.
The files you need are here github.com/TomKnn/EindopdrachtMood...
And here is the main.js where I want to import the function.
github.com/TomKnn/EindopdrachtMood...
Pls let me know if something is unclear, many thanks!!
Tom
how comes you are invoking the "address" function without any parenthesis.
const printAddress = async () => {
const a = await address;
console.log(a);
};
total noob question here i guess, but would appreciate if someone can direct me to the right resource to learn this further , tia
because "address" in not a function here, but a variable to which the return value of the fetch request is assigned to
thanks for the reply.
Just one last question, if the "address" is a variable which stores the result of the fetch query, then what is triggering the fetch call ?.
once the interpreter ((like the JavaScript interpreter in a web browser) ) reaches the code with the fetch() function, it adds it to the call stack list. We just assign the result (the return value) of this async function in order to have access to it later.
The call stack order is really easy to test. If we update the code like this:
The result in the console will be:
RUN
PRINT ADDRESS
FETCH RETURN
The fetch function is being executed without any additional trigger or call. But we get its result after all other functions because fetch takes more time to complete its job. And as fetch is an asynchronous function, it's not blocking other functions.
thank you so much
Well, that is not exactly a solution as far as I can understand the problem (or confusion if you will) that was supposed to be solved by this article.
In the first part, you are showing an assignment to a var "address" and then you are trying to log it, which returns a promise as was expected, right?
Then you are proposing to add another function and call "address" from the inside, but you DO NOT log "printAddress", you invoke it. You got logged data, but the same result could have been achieved by just logging the response in "address".
As for "printAddress", by making in async function you have converted it to a promise, so by trying to log it, you will get the same result as logging "address".
Correct me if I'm mistaken, but it seems to me your solution doesn't solve anything but only making the code bigger. The response date was logged from inside the promise and hasn't been extracted in any way.
The problem that you have tried to solve is definitely bothering a number of junior devs (me also as one of those juniors), however, I see no solution here. Moreover, I am, frankly, displeased to see unchecked, untrustworthy content here, no offense.
The blog post consists of two parts:
Probably you have another use case, that's why this solution doesn't meet your needs. If you could share the problem that you are trying to solve, maybe we could discuss the possible solution.
Thank you so much! You have no idea how helpful this simple guide was for me. I really mean it. Thank you for taking the time to write this.
Thank you for such nice words! Happy to help!
Hello friend, I'm a Brazilian guy who spent 10 hours trying to understand why I couldn't get the result from my API, I spent hours trying to find an answer on the internet on all possible pages and I didn't understand or found any answer, I ended up on your blog and wanted to thank you , I signed up on this site just to comment that your job saved me in every way possible, as I needed it to get a job! please don't stop your blog! you are awesome! and teaches very well! thanks!
Hi Johnny! Thank you for such nice words! It means a lot to me!
The main reason why I write blog posts is to share my knowledge with fellow developers so we could help each other.