0

I have the following timed function to periodically refetch credentials from an external API in your usual movie fetching IMDB clone app:

  // This variable I pass later to Apollo Server, below all this code.
  let tokenToBeUsedLater: string;

  // Fetch credentials and schedule a refetch before they expire
  const fetchAndRefreshToken = async () => {
    try {

      // fetchCredentials() sends an http request to the external API:
      const { access_token, expires_in} = await fetchCredentials() as Credentials;

      tokenToBeUsedLater = access_token;

      // The returned token expires, so the timeout below is meant to recursively
      // loop this function to refetch fresh credentials shortly before expiry.
      // This timeout should not stop the app's execution, so can't await it.
      // Have also tried the following with just setTimeout() and 
      // no `return new Promise()`but it throws a 2nd identical error.
      return new Promise(resolve => setTimeout(() => {
        resolve(fetchAndRefreshToken());
      }, expires_in - 60000));

    } catch (err) { throw new Error(err); }
  };

  fetchAndRefreshToken();  // <-- TypeScript error here

I have tried rewriting it in a thousand ways, but no matter what I do I get a Promises must be handled appropriately or explicitly marked as ignored with the 'void' operator error.

I can get rid the error by by:

  • Using .then().catch() when calling refreshToken(). Not ideal since I don't want to mix it with async and try/catch.
  • Putting a void operator ahead of refreshToken(). Feels like cheating.
  • Putting await ahead of refreshToken(). Essentially breaks my app (pauses execution of the app while waiting for the token to expire, so users in the frontend can't search for movies).

Any idea about how to solve this?

Also, any suggested resources/topics to study about this? Because I had a similar issue yesterday and I still can't figure this one out despite having already solved the other one. Cheers =)

HigoChumbo
  • 858
  • 2
  • 9
  • 23
  • 1
    Have you removed some real HTTP-calls and replaced it with `setTimeout` to simulate async behaviour here? If so, please include the real calls instead. `return new Promise` looks like an anti-pattern since your real call should return a promise..? – Daniel B Apr 12 '21 at 13:28
  • Not really, my http-calls are in the `fetchCredentials()` function. The thing is the credentials return with an expiry window in miliseconds, so the `refreshToken` function with the `setTimeout()` inside is meant to refetch the credentials at the end of that window. The app is actually running fine, I just want to figure out the correct way of doing this so that TS doesn't complain. –  HigoChumbo Apr 12 '21 at 13:43
  • 2
    I still think the more logical approach would be to create an auth flow that if the http call returns a `401 Unauthorized`, the refresh flow should be started instead. It's a bit tricky to help you out since your question really begs for the answer that an `await refreshToken()` should do the trick, but we have no idea what "breaks my app" really means... – Daniel B Apr 12 '21 at 14:13
  • Sorry about that, I'll explain a bit better. It's a backend for your usual IMDB clone. The backend fetches movies from an external API and sends them to a frontend where many concurrent users are requesting movies. The reason why I'm trying to avoid `await` or refreshing only on a `401` is to prevent that users are left temporarily with no service while refetching the credentials, so I was trying to refresh credentials a minute ahead of expiry (hence the `expires_in - 60000`). Await breaks the app because it pauses execution for one hour waiting for the token to expire, so users can't search. –  HigoChumbo Apr 12 '21 at 14:34
  • "*Putting `await` ahead of `refreshToken()` essentially pauses execution of the app while waiting for the token to expire.*" - it's unclear what the code surrounding that `refreshToken()` does (is it even an `async function`?) and why it starts the token refetching loop. – Bergi Apr 12 '21 at 17:12
  • "*Putting a `void` operator ahead of `refreshToken()` feels like cheating.*" - well, rightfully so, because it means you are ignoring exceptions. How (and where) do you want to handle errors from the `fetchCredentials()` call? – Bergi Apr 12 '21 at 17:14
  • `fetchCredentials()` throws an Error passed to express Middleware. As for `fetchAndRefreshToken()`: yes, it is async, it calls `fetchCredentials()` (also async) at the start of the app and then recursively schedules a periodic recall before the returned token expires (`fetchCredentials() returns the token alongside an expiry window in miliseconds (`expires_in`)). In short, it's a function for polling the API every hour or so to get fresh credentials. I hope that answers your question, let me know if you meant something else =) I have also streamlined the code in my post a bit. –  HigoChumbo Apr 12 '21 at 17:50

0 Answers0