When I learnt about promises and asynchronous coding. The biggest time consumer was when I tried calling an asynchronous function inside a normal function (synchronous code). I was hoping to use the asynchronous function to get the promises which had the values I was looking for. But what I got was [object Promise]
. The asynchronous function was working fine. But when I called the async function inside a normal function it would return [object Promise]
. So no promises!
Why?
You are getting [object Promise]
as value because you are calling an asynchronous function inside a synchronous code. This means the synchronous code runs line by line giving no room for asynchronous code to wait and give a response (the promise). You have to wrap an asynchronous function around the asynchronous function you are calling to get the desired result.
In the below code, we are creating a function which returns a promise. The below callOpenWeather()
function uses fetch API to call the OpenWeatherMap api so that we can get a value called feels_like
. This feels_like
is a numerical value.
//Async function to fetch the weather info
// using OpenWeatherMap API
const callOpenWeather = async (url) => {
// Try part when successful return of promise
try {
// Calls the API url, parses to JSON, returns
// feels_like value which is a numerical value.
let callJson = await fetch(url, {mode: 'cors',});
let loadJson = await callJson.json();
return loadJson.main.feels_like;
// Catch part if promise returns an error
} catch(error) {
return error;
}
}
Incorrect way: Asynchronous function inside a synchronous code
Now let's write a function which will interact with the above callOpenWeather()
. The below code won't work. When you need to call callOpenWeather()
function which returns a promise, you cannot call it inside a synchronous code as shown below. It will return [object Promise]
as response.
// DOM function to get the data from input and
// use checkWeather function to get the data displayed.
const displayWeather = () => {
const submitButton = document.getElementById('button');
const inputValue = document.getElementById('search');
const infoBox = document.getElementById('info-box');
submitButton.addEventListener('click', () => {
infoBox.style.display = 'grid';
// Use an api key of openweathermap instead of ${apiKey}
// to make this code work.
infoBox.innerText = callOpenWeather(`http://api.openweathermap.org/data/2.5/weather?q=${inputValue.value}&APPID=${apiKey}`);
infoBox.style.boxShadow = '0 0 2px 0 #d3d3d3';
});
}
displayWeather();
This is because when infoBox.innerText
calls the callOpenWeather()
function, the displayWeather()
function is still a normal synchronous function. This means the lines are executed line by line and does not wait for the value from callOpenWeather()
which is an asynchronous function. To call callOpenWeather()
and get the value (a promise), make it asynchronous. You can do this by wrapping callOpenWeather()
inside an async function using async/await method as shown below. This will make an api call to OpenWeatherMap API and wait for the result so that the result can be set and displayed in infoBox.innerText
.
Correct way: Asynchronous function wrapped with an asynchronous function
We are wrapping an async function with the eventlistener for the click event. This will let callOpenWeather()
function to run properly and wait until it returns a response as provided by the OpenWeatherMap API. The below solution uses async/await method. You can see the usage of await
keyword which waits for a response from the callOpenWeather()
function and returns a promise.
// DOM function to get the data from input and
// use checkWeather function to display data.
const displayWeather = () => {
const submitButton = document.getElementById('button');
const inputValue = document.getElementById('search');
const infoBox = document.getElementById('info-box');
// Users "async" keyword on the click event so as to
// make the await at `callOpenWeather()` to wait
// and give back a response (the promise)
submitButton.addEventListener('click', async () => {
infoBox.style.display = 'grid';
// Use an api key of openweathermap instead of ${apiKey}
// to make this code work.
infoBox.innerText = await callOpenWeather(`http://api.openweathermap.org/data/2.5/weather?q=${inputValue.value}&APPID=${apiKey}`);
infoBox.style.boxShadow = '0 0 2px 0 #d3d3d3';
});
}
displayWeather();
This is how you could get the value from your asynchronous code when you are stuck with [object Promise]
as your output. This is one of those scenarios where if you think about it, it makes total sense. But our synchronous mind could find it tricky.
Found an error? Have feedback on my writing? DM me on Twitter @unsungNovelty.
This post was first published at https://www.unsungnovelty.org under the title "initLogs 4: Why am I getting [object Promise] when calling async function in JavaScript".