DEV Community

Nikhil
Nikhil

Posted on • Edited on • Originally published at unsungnovelty.org

initLogs 4: Why am I getting [object Promise] when calling an asynchronous function in JavaScript?

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!

An image with a woman saying no promises to a man

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.

Initial state of the web page with an input and a submit button

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

Returns object Promise when calling an async function inside synchronous code

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();

Enter fullscreen mode Exit fullscreen mode

Final output

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".

Top comments (2)

Collapse
 
miketalbot profile image
Mike Talbot ⭐

You could just make the click handler async in this case.

submitButton.addEventListener('click', async () => {

            infoBox.style.display = 'grid';
            infoBox.innerText = await callOpenWeather(`http://api.openweathermap.org/data/2.5/weather?q=${inputValue.value}&APPID=${apiKey}`); // Use an api key of openweather api instead of ${apiKey} to make this code work.
            infoBox.style.boxShadow = '0 0 2px 0 #d3d3d3';    

    });
Enter fullscreen mode Exit fullscreen mode
Collapse
 
unsungnovelty profile image
Nikhil • Edited

TIL! That makes the code a little more clean. Let me make the change right away. Thanks @miketalbot !