Why is function in then not receiving object

I understand the notion of Promises, but something seems not to be completely clear to me.

I have the following html:

<!DOCTYPE html> .......
<div id="map"></div>
<script
        src="https://maps.googleapis.com/maps/api/js?key=GOOGLE_API&callback=initialize"
        async></script>
</body>
</html>

<script src="js/currentRoute.js"></script>

I have the following JS code:

async function getCurrentUserCoordinates() {
    console.log("Am getCurrentUserCoordinates")

    const url = baseUrl + `/SOME_REST_API_URL`;

    if (checkAdminOrTechRights(parseToken(getToken()))) {
        await fetch(url, {
            method: 'GET',
            headers: {
                'Accept': 'application/json',
                'Authorization': `Bearer ${getToken()}`
            },
        })
            .then((response) =>
                response
                    .json()
            )
            .then(terminals => {
                let locations = [];
                terminals.forEach(terminal => {
                    locations.push({
                        lat: terminal.latitude,
                        lng: terminal.longitude,
                        name: terminal.name,
                        location: terminal.location
                    })
                })
                console.log(locations) // ALL IS PRINTING OK HERE
                return locations;
            }).catch((err) => {
                console.error(err);
            })
    }
}

function initMap(locations) {
    console.log("Am initMap")
    console.log("printing locations: " + locations) // I HAVE UNDEFINED HERE
    const map = new google.maps.Map(document.getElementById("map"), {
        zoom: 12,
        center: {lat: 0.123123123123, lng: 0.123123123123},
    });
    const infoWindow = new google.maps.InfoWindow({
        content: "",
        disableAutoPan: true,
    });

    const markers = locations.map((terminal, i) => {
        const label = `${terminal.name}, ${terminal.location}`;
        const marker = new google.maps.Marker({
            position: terminal,
            label,
        });

        marker.addListener("click", () => {
            infoWindow.setContent(label);
            infoWindow.open(map, marker);
        });
        return marker;
    });

    new markerClusterer.MarkerClusterer({markers, map});
}


async function initialize() {
    console.log("Am initialize")
    getCurrentUserCoordinates()
        .then(locations => initMap(locations))
        .then(() => console.log("Am done"))
        .catch((err) => {
            console.error("ERROR!!! " + err);
        })
}

I have the following logs:

Am initialize

currentRoute.js:2 Am getCurrentUserCoordinates

currentRoute.js:28 (26) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]0: {lat: 1.123123123, lng: 1.123123123, name: 'TERM-0001', location: 'LOCATION'}1: {lat: 1.123123123, lng: 1.123123123, name: 'TERM-0099', location: 'LOCATION'}2: ............ 25: {lat: 1.123123123, lng: 1.123123123, name: 'TERM-0023', location: 'LOCATION'}length: 26[[Prototype]]: Array(0)

currentRoute.js:37 Am initMap

currentRoute.js:38 printing locations: undefined

So in my view initMap() has to be called when the getCurrentUserCoordinates() returns the result (locations). Inside the functions am getting locations and as seen on the logs they are printed. But the locations are passed as undefined inside the initMap functions.

What am I not getting here?

Thank you.


The main problem with your code was that you were calling return from inside a nested function within then which you had assumed would return from the outer method getCurrentUserCoordinates but that is not the case.

Your functionality can be hugely simplified by not mixing async/await with then - the former is easier to manage. The code also benefits from replacing the clunky array + forEach with a simple map over the array.

async function getCurrentUserCoordinates() {
    console.log("Am getCurrentUserCoordinates")

    const url = baseUrl + `/SOME_REST_API_URL`;

    if (checkAdminOrTechRights(parseToken(getToken()))) {
      try{
        const result = await fetch(url, {
            method: 'GET',
            headers: {
                'Accept': 'application/json',
                'Authorization': `Bearer ${getToken()}`
            },
        });
        const terminals = await result.json();
        let locations = terminals.map( terminal => ({
                lat: terminal.latitude,
                lng: terminal.longitude,
                name: terminal.name,
                location: terminal.location
        }))
        console.log(locations) // ALL IS PRINTING OK HERE
        return locations;
       }
       catch(err){
        console.error(err);
       }        
    }
}

* Note the return locations in this code is now in the outer scope of the method itself, however as the method is async it actually returns a Promise so you must await it later on (see below). H/T @JeremyThille

The same is true later in your code

async function initialize() {
    console.log("Am initialize")
    try{
        const currentUserCoords = await getCurrentUserCoordinates();
        const locations = initMap(currentUserCoords);
        console.log("Am done"))
    }
    catch(err){
        console.error("ERROR!!! " + err);
    }
}