0

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...

Ben Owen
  • 113
  • 1
  • 10
  • what `getChatData` supposed to do? – Nur May 29 '21 at 21:36
  • What is `websocket`? It's `send` method seems to throw that `INVALID_STATE_ERR`, which you're not handling anywhere. – Bergi May 30 '21 at 14:37
  • its a normal java script web socket. I have all the onclose, onopen, onmessage functions set up and they work. – Ben Owen May 30 '21 at 15:30
  • https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send says it throws that... – Ben Owen May 30 '21 at 15:31
  • I am going to wrap all my sends in try catches and see if that helps. I get different errors all the time. TBH I am checking the state before I send so I don't get why it is being called...unless it is closing after or gets interrupted during send – Ben Owen May 30 '21 at 15:34
  • To avoid bugs, You should KISS (keep it simple, stupid), Also don' t forget to handle error from async code. – Nur May 30 '21 at 16:31
  • I normally don't have this problem. The main issue is I am new to React Native and JS. I have been having trouble handling the logic and state properly as JS and React do all sorts of weird things and have loads of seemingly unintuitive limitations. Also, since wrapping my websocket.sends I have not had this error pop up, but I also have not had the catch called. I did put in your refactor, but I did refactor myself. That code was a mess, much cleaner now. This is the root file so it needs to load everything. – Ben Owen May 31 '21 at 10:13

1 Answers1

2

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.

Nur
  • 2,361
  • 2
  • 16
  • 34
  • When I get back from work I will try this. The only issue is that it does not occur all the time. Usually it happens when something else goes wrong or I reload the app. At least, it seems to occur then. So I have no way of being sure. – Ben Owen May 30 '21 at 08:08
  • getChatData, fetches all the chats and last 30 messages used by the chat system. This i could exists in the root file so it is essentially syncing with the backend. If it cannot load the data from the server it fetches what was last stored/cached in encrypted storage...in fact I will add that to my question when I get back as there is an async call to encrypted storage, but that could existed before the websockets initialisation stuff and I had no issue. – Ben Owen May 30 '21 at 08:09
  • also if `getChatData` is an async operation, then use `await` keyword before it, Also report your result (if there is any error)... – Nur May 30 '21 at 11:41
  • I just updated my question with getChatData. As you can see it is async, but I only added that to try and stop the error. I am going to test your code now. – Ben Owen May 30 '21 at 14:36
  • @Ben Owen ,I checked and refactored your other code, Try this and if you got any error, then its an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem), feel free to inform us if you have other questions – Nur May 30 '21 at 16:35
  • I hope I am not going to get charged for these lessons. I think I need to have think about how I organise this stuff. These functions kind of grew and changed as and when I needed them to. I think I need to go rewrite them properly. I can't just paste your code in right away for as I need to understand it before I do that. I don't really understand what you are doing with the async stuff. – Ben Owen May 31 '21 at 10:18
  • Yes, I need to go off an get my head around all this aysnc stuff and come back and reorganise the whole root file. Looking at it now this was inevitably going to turn out like this. – Ben Owen May 31 '21 at 10:23
  • _"I need to understand it before I do that. I don't really understand what you are doing with the async stuff."_ - which part you didn't understand ? – Nur May 31 '21 at 12:13
  • All the async and await parts. I tried to understand it before, but I can't get my head around it. – Ben Owen May 31 '21 at 13:38
  • @Ben Owen, I update my answer, Does it clear now? – Nur May 31 '21 at 15:45
  • Yes, that makes a lot more sense. – Ben Owen May 31 '21 at 17:32
  • now that I get this stuff I actually really like how you format the fetch request. So much cleaner than what I have been doing. On the one hand thank you for showing me this, on the other, god damn it I now have to go a clean up loads of other code XD. – Ben Owen May 31 '21 at 17:35