0

When mapping the results of a promise to a array variable, the map awaits the promise, but the array variable is returned as an unresolved promise before the value mapping occurs.

Code with calling api with axios.get() and mapping results to a variable:

const refreshTableData = async () => {
    
    const clientState = getAccessTokenAndUrl();
    let rowDataArray = [];

    try {
        let healthStatusResults = await axios.get(
            {
                baseUrl: clientState.apiUrl,
                url: 'some/api/url/path',
                responseType: 'json',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': ('Bearer ' + clientState.jssAccessToken)
                },
                params: {
                    isValid: false,
                    systemName: 'some_system_name'
                }
                // potentially add validate status to communicate 400 and 500 level errors
            }
        );

        healthStatusResults.devices.map(
            (device) =>{
                console.log('device: ', device);
                rowDataArray.push([
                    device.deviceId,
                    device.isValid
                ]);
        });
    } catch (error) {
        // potentially add error message react component here
        const errorMessage = 'Error - healthStatus api response error: ' + error;
        console.log(errorMessage);
        // alert(errorMessage);
    }

    console.log('refreshTableData - rowDataArray: ', rowDataArray);
    return (rowDataArray);
}

Function using the result returned from refreshTableData:

const generateRows = (rowDataArray) => {
    console.log('DevicesStatusTable - rowDataArray: ', rowDataArray);
    let rows = [];
    
    for (let i = 0; i < rowDataArray.length; i += 1) {
        let columnDataArray = [rowDataArray[i][0], rowDataArray[i][1]];
        rows.push(<DeviceStatusRow columnDataArray={columnDataArray} key={'statusRow-' + i} />);
      };

    return rows;
};

React component DeviceStatusTable calling generate rows:

export const DeviceStatusTable = (props) => {
    return (
        <div>
            <table>
                <DeviceStatusHeader systemName={props.systemName} />
                <tbody>
                    {generateRows(props.rowDataArray)}
                </tbody>
            </table>
        </div>
    );
};

React component HealthDashboard calling DeviceStatusTable and passing in results of refreshTableData() as props:

export const HealthDashboard = () => {
    return (
        <div>
            <DeviceStatusTable systemName="Minestar Edge" rowDataArray={refreshTableData()}/>
        </div>
    );
};

Notice the order and values of the console.log() statements:

tests/HealthDashboard/HealthDashboard.test.js
  ● Console

    console.log
      DevicesStatusTable - rowDataArray:  Promise { <pending> }

      at generateRows (js/DeviceStatusTable/DeviceStatusTable.jsx:19:13)

    console.log
      device:  {
        deviceId: 'device_1',
        isValid: true,
        systemName: 'minestar_edge',
        timeDiscrepancy: 10
      }

      at map (js/HealthDashboard/HealthDashboard.jsx:39:25)
          at Array.map (<anonymous>)

    console.log
      device:  {
        deviceId: 'device_2',
        isValid: false,
        systemName: 'minestar_edge',
        timeDiscrepancy: 20
      }

      at map (js/HealthDashboard/HealthDashboard.jsx:39:25)
          at Array.map (<anonymous>)

    console.log
      refreshTableData - rowDataArray:  [ [ 'device_1', true ], [ 'device_2', false ] ]

      at _callee$ (js/HealthDashboard/HealthDashboard.jsx:52:13)

I expected that the rowDataArray value wouldn't be returned to generateRows until it was resolved, but instead it gets passed back as a promise. Meanwhile the map process does await the promise resolution and the associated and subsequent logs return after the log from the generateRows function.

Why is rowDataArray being returned from refreshTableData before the healthStatusResults promise filling the rowDataArray is resolved?

Matt Brauer
  • 211
  • 4
  • 13
  • 2
    `refreshTableData` is an async function. That means it returns a Promise. You have to use `await refreshTableData()` or `refreshTableData().then(...)` – Barmar May 19 '21 at 22:06
  • Show the code that's calling `generateRows()` with the result of `refreshTableData()`. – Barmar May 19 '21 at 22:07
  • Added react components calling and passing data to generateRows() – Matt Brauer May 19 '21 at 22:20
  • 1
    "*the map awaits the promise*" - no. The `await` keyword does that, nothing to do with the `map` method. – Bergi May 20 '21 at 00:21

1 Answers1

-2

refreshTableData is an async function, so it returns a Promise, and that's what you're assigning to rowDataArray.

Change

{generateRows(props.rowDataArray)}

to

{props.rowDataArray.then(generateRows)}
Barmar
  • 741,623
  • 53
  • 500
  • 612