0

I am calling following function to get a list of saved cards. fetch function return a Promise Object. I want to convert it to a string[] and return. Is this a possible thing to do?

const fetchSavedCards = (): string[] => {
return fetch("/fetchSavedCards")
    .catch((error: Error) => {
        throw new Error(error.message);
    })
    .then((response) => {
        return response.json();
    })
    .then((cards: string[]) => {
        return cards;
    });

};

The returned result set is then need to be displayed in a web page. But .map() function does not work with Promise object.

    <CardContent>
    <label> Card Selection  </label>
    <div className="container" id="cards">
        {Service.fetchSavedCards().map(
            (card) => <label>
                <input type="radio" name="selectedCard" value="test" onChange={this.handleCardSelectionChange}/>
                    {card}
            </label>)}
    </div>
    ...
</CardContent>
Ruchira Nawarathna
  • 1,137
  • 17
  • 30

3 Answers3

3

You want to convert an async function to sync function, that's not a good idea and you can't do that. I guess maybe you just don't know how to use fetchSavedCards correctly after you define it like I before.

const fetchSavedCards = (): Promise<string[]> => {
  return fetch("/fetchSavedCards")
    .then((response) => {
        return response.json();
    })
    .then((cards: string[]) => {
        return cards;
    })
    .catch((error: Error) => {
        throw new Error(error.message);
    });
};

// how to use fetchSavedCards correctly
fetchSavedCards().then(cards => handleCards(cards));

BTW, it is better to put the .catch() in the last.

If you want to use these data to render the view in the react, you should save the returned data in the state, and render the view by state.

Full example (by react hooks):


const fetchSavedCards = (): Promise<string[]> => {
  return fetch('/fetchSavedCards')
    .then(response => {
      return response.json()
    })
    .then((cards: string[]) => {
      return cards
    })
    .catch((error: Error) => {
      throw new Error(error.message)
    })
}

function Cards() {
  const [cards, setCards] = useState<string[]>([])

  useEffect(() => {
    fetchSavedCards().then(cards => setCards(cards))
  }, [])

  return (
    <CardContent>
      <label> Card Selection </label>
      <div className="container" id="cards">
        {cards.map(card => (
          <label>
            <input
              type="radio"
              name="selectedCard"
              value="test"
              onChange={this.handleCardSelectionChange}
            />
            {card}
          </label>
        ))}
      </div>
      ...
    </CardContent>
  )
}
Spark.Bao
  • 5,573
  • 2
  • 31
  • 36
1

Technically, you can. Consider this:

const fetchSavedCards = (): string[] => {
  const result: string[] = [];

  fetch("/fetchSavedCards")
    .catch((error: Error) => {
        throw new Error(error.message);
    })
    .then((response) => {
        return response.json();
    })
    .then((cards: string[]) => {
        result.length = 0;
        return cards.forEach((item, i) => {
          result[i] = item;
        });
    });

  return result;
};

You can of course return the premature result array eagerly, then wait for the promise to resolve later and fill-in the element of result asynchronously. But is it a desirable solution to you?

Maybe tell us about the real problem you encountered.

hackape
  • 18,643
  • 2
  • 29
  • 57
  • You get an empty array at first. Wait a bit, then check later you'll see it's non-empty. Not sure why you think pass in as param should change anything :-/ – hackape Mar 10 '20 at 08:25
  • @hackape - I need to display the returned result set in a web page and I have edited the question with the front-end code. There .map() does not work with Promise object – Ruchira Nawarathna Mar 10 '20 at 09:51
  • @Ruchira Looks like you're using react. Do you use hook API or traditional class component API? – hackape Mar 10 '20 at 10:02
  • @hackape - Yes I'm using react and it is a traditional class component API – Ruchira Nawarathna Mar 10 '20 at 10:09
0

I had the same issue recently and wrote this type to help with that

export type UnpackPromise<T> = T extends {
    then(onfulfilled?: (value: infer U) => unknown): unknown;
} ? U : never;

basically a combination of generics, conditional types and infer

in your case I would use the ReturnType Utility for the function and call the UnpackPromise type with whatever that returns

Ch3micaL
  • 311
  • 1
  • 15