Search code examples
javascriptfirebasereact-nativesettimeoutthrow

How to throw an error in the outer function from an inner async function like setTimeout in JavaScript?


i want to sign in to in my react native app using firebase authentication. For example, to login with email and password i have the following function, which is basically a redux action:

export const userSignin = (email, password) =>
  async dispatch => {
    try {
      dispatch({ type: 'auth_attempt_started' })
      const user = await firebase.auth().signInWithEmailAndPassword(email.trim(), password)
      saveUserDataToAsyncStorage(user)
      if (!TASKS_SORT_BY && !TASKS_SORT_ORDER)
        dispatch(getTasksSortData(user.user.uid))
      if (!EMPLOYEES_SORT_BY && !EMPLOYEES_SORT_ORDER)
        dispatch(getEmployeesSortData(user.user.uid))
      if (!ACCOUNTS_SORT_BY && !ACCOUNTS_SORT_ORDER)
        dispatch(getAccountsSortData(user.user.uid))
      goToMain()
      setTimeout(() => {
        dispatch({
          type: 'user_signedin',
          payload: user
        })
      }, 100);
    }
    catch (err) {
      dispatch({
        type: 'auth_error',
        payload: err.toString()
      })
    }
  }

This function throw an error if something went wrong with the signin process. The problem is when the internet connection is slow, the signin process take a long time before the success or the failure, sometimes around 30 seconds which is very bad for the user experience.

The question is:

How can i throw an error after sometimes maybe 10 seconds if the signin process does not complete?

Thankyou!


Solution

  • In general, it can be achieved with setTimeout.

    componentDidMount() {
      this.setTimeout( () => {
         this.doThisAfterTenSeconds();
      },10000);
    }
    
    doThisAfterTenSeconds() {
       // Do something
    }
    

    In this particular scenario,

    export const userSignin = (email, password) =>
      async dispatch => {
        try {
          // Auth attempt has started
          dispatch({ type: 'auth_attempt_started' })
    
          // Lets add a 10 second timeout
          let hasTimedOut = false
          setTimeout(() => {
              hasTimedOut = true
              // Display message here or throw "Connection timed out"
          }, 10000)
    
          // Now lets await the authentication
          const user = await firebase.auth().signInWithEmailAndPassword(email.trim(), password)
          // This code should not be run if auth attempt timed out. If we throw an error the check should not be neccessary...
          if (!hasTimedOut)
          {
             saveUserDataToAsyncStorage(user)
             if (!TASKS_SORT_BY && !TASKS_SORT_ORDER)
               dispatch(getTasksSortData(user.user.uid))
             if (!EMPLOYEES_SORT_BY && !EMPLOYEES_SORT_ORDER)
               dispatch(getEmployeesSortData(user.user.uid))
             if (!ACCOUNTS_SORT_BY && !ACCOUNTS_SORT_ORDER)
               dispatch(getAccountsSortData(user.user.uid))
             goToMain()
             setTimeout(() => {
               dispatch({
                 type: 'user_signedin',
                 payload: user
               })
             }, 100);
             }
           }
           catch (err) {
             dispatch({
               type: 'auth_error',
               payload: err.toString()
             })
           }
         }