TWLATL
TWLATL

Reputation: 2879

Receiving Promise {<pending>} back from .then()

I have an API call in api.js:

 export const getGraphData = (domain, userId, testId) => {
   return axios({
     url: `${domain}/api/${c.embedConfig.apiVersion}/member/${userId}/utests/${testId}`,
     method: 'get',
   });
 };

I have a React helper that takes that data and transforms it.

import { getGraphData } from './api';

const dataObj = (domain, userId, testId) => {

  const steps = getGraphData(domain, userId, testId)
  .then((result) => {
    return result.attributes;
  });

  console.log(steps);

  // const steps = test.get('steps');
  const expr = /select/;

  // build array of steps that we have results in
  const resultsSteps = [];

  steps.forEach((step) => {
    // check for types that contain 'select', and add them to array
    if (expr.test(step.get('type'))) {
      resultsSteps.push(step);
    }
  });

  const newResultsSteps = [];

  resultsSteps.forEach((item, i) => {
    const newMapStep = new Map();
    const itemDescription = item.get('description');
    const itemId = item.get('id');
    const itemOptions = item.get('options');
    const itemAnswers = item.get('userAnswers');
    const newOptArray = [];
    itemOptions.forEach((element) => {
      const optionsMap = new Map();
      let elemName = element.get('value');
      if (!element.get('value')) { elemName = element.get('caption'); }
      const elemPosition = element.get('position');
      const elemCount = element.get('count');

      optionsMap.name = elemName;
      optionsMap.position = elemPosition;
      optionsMap.value = elemCount;
      newOptArray.push(optionsMap);
    });
    newMapStep.chartType = 'horizontalBar';
    newMapStep.description = itemDescription;
    newMapStep.featured = 'false';
    newMapStep.detailUrl = '';
    newMapStep.featuredStepIndex = i + 1;
    newMapStep.id = itemId;
    newMapStep.isValid = 'false';
    newMapStep.type = 'results';
    const listForNewOptArray = List(newOptArray);
    newMapStep.data = listForNewOptArray;
    newMapStep.userAnswers = itemAnswers;
    newResultsSteps.push(newMapStep);
  });

  return newResultsSteps;
};

export default dataObj;

The issue is steps, when logged outside the .then() returns a Promise {<pending>}. If I log results.attributes inside the .then(), I see the data fully returned.

Upvotes: 7

Views: 34416

Answers (3)

Dyo
Dyo

Reputation: 4464

You have 2 options to transform your fetched data :

1st option : create a async function that returns a promise with the modified data :

const dataObj = (domain, userId, testId) => {
  return getGraphData(domain, userId, testId).then((result) => {
    const steps = result.attributes;
    const expr = /select/;
    // build array of steps that we have results in
    const resultsSteps = [];

    steps.forEach((step) => {
      // check for types that contain 'select', and add them to array
      if (expr.test(step.get('type'))) {
        resultsSteps.push(step);
      }
    });

    const newResultsSteps = [];

    resultsSteps.forEach((item, i) => {
      const newMapStep = new Map();
      const itemDescription = item.get('description');
      const itemId = item.get('id');
      const itemOptions = item.get('options');
      const itemAnswers = item.get('userAnswers');
      const newOptArray = [];
      itemOptions.forEach((element) => {
        const optionsMap = new Map();
        let elemName = element.get('value');
        if (!element.get('value')) {
          elemName = element.get('caption');
        }
        const elemPosition = element.get('position');
        const elemCount = element.get('count');

        optionsMap.name = elemName;
        optionsMap.position = elemPosition;
        optionsMap.value = elemCount;
        newOptArray.push(optionsMap);
      });
      newMapStep.chartType = 'horizontalBar';
      newMapStep.description = itemDescription;
      newMapStep.featured = 'false';
      newMapStep.detailUrl = '';
      newMapStep.featuredStepIndex = i + 1;
      newMapStep.id = itemId;
      newMapStep.isValid = 'false';
      newMapStep.type = 'results';
      const listForNewOptArray = List(newOptArray);
      newMapStep.data = listForNewOptArray;
      newMapStep.userAnswers = itemAnswers;
      newResultsSteps.push(newMapStep);
    });
    return newResultsSteps;
  });
};

With es7 async/await syntax it should be :

const dataObj = async (domain, userId, testId) => {
    const result = await getGraphData(domain, userId, testId);
    const steps = result.attributes;
    ... modify the data
}

Then keep in mind that this function returns a promise, you'll need to wait for it to get the result, example in a react component :

componentDidMount(){
   dataObj('mydomain', 'myuserId', 'mytestId').then((res) => {
       this.setState({ data: res });
   }
}

The component will update when the promise is resolve, you can then use the data (you'll need to handle the undefined data state in render method)

2nd option : Create a sync function to modify the data :

const dataObj = (steps) => {
    const expr = /select/;
    const resultsSteps = [];

    steps.forEach((step) => {
    ...
    }
    return newResultsSteps;
};

To have the same result as option 1 in our component we'll use it like this :

componentDidMount(){
   getGraphData('mydomain', 'myuserId', 'mytestId').then((res) => {
       const modifiedData = dataObj(res);
       this.setState({ data: modifiedData });
   }
}

Upvotes: 1

Jamiec
Jamiec

Reputation: 136094

You need to wait until your async call is resolved. You can do this by chaining on another then:

getGraphData(domain, userId, testId)
  .then((result) => {
    return result.attributes;
  })
  .then(steps => {
     // put the rest of your method here
  });

You can also look at async/await if your platform supports it which would allow code closer to your original

const steps = await getGraphData(domain, userId, testId)
  .then((result) => {
    return result.attributes;
  });

// can use steps here

Upvotes: 6

Mathew Berg
Mathew Berg

Reputation: 28750

That's how promises work. The data is not ready when you are trying to use it so you should move all your processing into the .then. The reason your variable is a Promise {<pending>} is because you can chain other things onto it.

Something like:

steps.then((steps) => {
    ...
});

Upvotes: 0

Related Questions