Ben Owen
Ben Owen

Reputation: 133

Unhandled Promise Rejection when trying to call external function from async function

The error message:

WARN  Possible Unhandled Promise Rejection (id: 1):
Error: INVALID_STATE_ERR
send@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:31745:26
initialiseWebsocket@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:100544:21
loadUserData$@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:100610:40
tryCatch@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7739:23
invoke@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7912:32
tryCatch@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7739:23
invoke@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7812:30
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7822:21
tryCallOne@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:28596:16
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:28697:27
_callTimer@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29113:17
_callImmediatesPass@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29152:17
callImmediates@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29370:33
__callImmediates@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3279:35
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3057:34
__guard@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3262:15
flushedQueue@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3056:21
flushedQueue@[native code]
invokeCallbackAndReturnFlushedQueue@[native code]

The useEffect that is being accused of being a problem:

  React.useEffect(() => {
    // Fetch the token from storage then navigate to our appropriate place
    const loadUserData = async () => {
      let userData;

      try {
        userData = await retrieveUserData();
      } catch (e) {}

      if(userData){
        dispatch({ type: 'RESTORE_USER_DATA', userData: userData });
        getChatData(userData, setChats, dispatch);

        if(userData && !websocketInitialised){
          console.log('web init called from *load user data*')
          setWebsocketInitialised(true)
          initialiseWebsocket(userData);
        }
      }
      else{
        dispatch({ type: 'RESTORE_USER_DATA_FAILED'});
      }
    };

    loadUserData();
  }, []);

The initialliseWebsocket function

  function initialiseWebsocket(userData){

    console.log('sending websocket initialisation data.');
    websocket.send(JSON.stringify({
      'action': 'init',
      'data' : {'token': userData.token}
    }));
  }

the useState that is used above

const [websocketInitialised, setWebsocketInitialised] = React.useState(false);



async function getChatData(userData, setChats, dispatch){
  console.log("fetching chat data");

  // if we fail to download chat data, pull the old one from FS
  const loadOldChatData = async () => {
    let chats;

    try {
      chats = await retrieveChats();
    } catch (e) {}

    if(chats){
      setChats(chats);
      console.log("loaded cached chat data")  ;
    }
    else{
      setChats([]);
    }
  };

  const onSuccess = (response) => {
    if(response['chats']){
      storeChats(response['chats']);
      setChats(response['chats']);
      console.log("chat data synced");
    }
    else{
      loadOldChatData();
    }
  };

  const onFailure = (response) => {
      loadOldChatData();
  };

  fetch(Settings.siteUrl + '/messenger/get_chats/', {
      method: "GET",
      headers: {
        "Content-type": "application/json; charset=UTF-8",
        "Authorization": "Token " + userData.token
      },
    })
    .then(response => response.json())
    .then(response => {onSuccess(response)})
    .catch(response => {onFailure(response)})
}

retrieveUseData() is most likely not the problem as this only started occuring after I added the other code.

Am I not supposed to use states like this or am I supposed to use the async key worked on functions? I tried that but I still have the same issue. You can see on the 4 line of the errors it mentions the 'initialiseWebsocket' function. I am guessing that is the route cause. I assume the solution will be some async version of it...

Upvotes: 0

Views: 918

Answers (1)

Nur
Nur

Reputation: 2473

This error tell us that you didn't or forget to handle error from async code.

I refectory your code a bit, Tell me if you got any error message from console.log(error);

React.useEffect(() => {
    // Fetch the token from storage then navigate to our appropriate place
    (async () => {
        try {
            let userData = await retrieveUserData();
            dispatch({ type: 'RESTORE_USER_DATA', userData });
            await getChatData(userData, setChats, dispatch);

            if (websocketInitialised) return;

            console.log('web init called from *load user data*')
            setWebsocketInitialised(true)
            initialiseWebsocket(userData);
        } catch (error) {
            console.log(error);
            dispatch({ type: 'RESTORE_USER_DATA_FAILED' });
        }
    })();
}, []);

And you should rename getChatData to setChatData, I also simplify those code also...

async function getChatData(userData, setChats, _dispatch) {
    try {
        let response = await fetch(Settings.siteUrl + '/messenger/get_chats/', {
            method: "GET",
            headers: {
                "Content-type": "application/json; charset=UTF-8",
                "Authorization": "Token " + userData.token
            },
        }),
            data = await response.json(),
            chats = data['chats'];

        if (!chats?.length) throw "empty chat data, pull the old one from FS";

        storeChats(chats);
        setChats(chats);
    } catch (_) {
        // if we fail to download chat data, pull the old one from FS
        await retrieveChats()
            .then(chats => setChats(chats))
            .catch(() => setChats([]))
    }
}

"I don't really understand what you are doing with the async stuff."

async/await is just syntax sugar of promise, It allow you to work with async operation in a synchronous manner, some rules of async/await

  1. In other to use await keyword, you need an async function.
  2. you can make any function asynchronous, just by adding async keyword
  3. async function always return promise

Lets see an example:

let delay = (ms, msg, bool) => new Promise((res, rej) => setTimeout(!bool ? res : rej , ms,msg));

This helper function create a promise for our example, it take 3 arguments, it take millisecond as 1st arg, to delay, 2rd is the message as payload. 3nd is Boolean; it true, then it will reject.

let delay = (ms, msg, bool) => new Promise((res, rej) => setTimeout(!bool ? res : rej, ms, msg));
let log = console.log;

async function myAsyncFn() {
   let hello = await delay(100, "hello,");
   let world = await delay(300, " world!");
   // we use a symbol '@' to indicate that, its from `myAsyncFn`
   log("@" , hello + world, "printed from async operation");
}

myAsyncFn();

log("As you can see that, this message print first");

// we are creating an async function and called immediately, In other to use `await keyword`
(async () => {
  try {
    let resolved = await delay(300,"resolved");
    console.log(">" , `it ${resolved}!`);
    // this will reject and catch via `try/catch` block; 
    let _ = await delay(600, "Error", true);
    log("It will not print!");
    // ...
  } catch (error) {
    log(">" , `we can catch "${error}" with try/catch, as like any sync code!`);
  }
})()

As you can see that with async/await its look like everything is synchronous right? even everything execute asynchronously!

You just need to use await keyword to make every async operation synchronous.

Upvotes: 2

Related Questions