【问题标题】:Unhandled Promise Rejection when trying to call external function from async function尝试从异步函数调用外部函数时未处理的 Promise Rejection
【发布时间】:2021-05-29 13:28:50
【问题描述】:

错误信息:

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]

被指责为问题的 useEffect:

  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();
  }, []);

initialliseWebsocket函数

  function initialiseWebsocket(userData){

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

上面使用的useState

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() 很可能不是问题,因为这只是在我添加其他代码后才开始发生的。

我不应该使用这样的状态,还是应该使用异步键来处理函数?我试过了,但我仍然有同样的问题。您可以在错误的第 4 行看到它提到了“initialiseWebsocket”函数。我猜这是路线的原因。我认为解决方案将是它的一些异步版本...

【问题讨论】:

  • getChatData 应该做什么?
  • 什么是websocket?这是send 方法似乎抛出了INVALID_STATE_ERR,你没有在任何地方处理。
  • 它是一个普通的 java script web socket。我已经设置了所有的 onclose、onopen、onmessage 函数并且它们可以工作。
  • 我将把我所有的发送都包装在 try catch 中,看看是否有帮助。我一直收到不同的错误。 TBH 我在发送之前检查状态,所以我不明白为什么它被调用......除非它在发送之后关闭或在发送期间被中断

标签: react-native es6-promise


【解决方案1】:

此错误告诉我们您没有或忘记处理来自异步代码的错误。

我稍微修改一下你的代码,如果你收到来自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' });
        }
    })();
}, []);

你应该把getChatData重命名为setChatData,我也简化了那些代码......

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([]))
    }
}

“我真的不明白你在用异步的东西做什么。”

async/await只是promise的语法糖,它允许你以同步的方式进行异步操作,async/await的一些规则

  1. 要使用await keyword,您需要一个async 函数。
  2. 您可以使任何函数异步,只需添加 async 关键字
  3. 异步函数总是返回promise

让我们看一个例子:

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

这个帮助函数为我们的例子创建了一个promise,它有3个参数,它把millisecond作为第一个参数,用于延迟,第二个是message作为有效载荷。第三位是Boolean;是真的,那么它会拒绝。

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!`);
  }
})()

正如您所见,async/await 看起来一切都是同步的,对吧?甚至一切都是异步执行的!

您只需要使用await 关键字来使每个异步操作同步。

【讨论】:

  • 下班回来我会试试这个。唯一的问题是它不会一直发生。通常当出现其他问题或我重新加载应用程序时会发生这种情况。至少,它似乎发生在那时。所以我无法确定。
  • getChatData,获取聊天系统使用的所有聊天和最近 30 条消息。这可能存在于根文件中,因此它本质上是与后端同步的。如果它无法从服务器加载数据,它会获取最后存储/缓存在加密存储中的内容......事实上,当我回来时,我会将它添加到我的问题中,因为有一个对加密存储的异步调用,但这可能存在在 websockets 初始化之前,我没有问题。
  • 另外如果getChatData是异步操作,那么在它之前使用await关键字,同时报告你的结果(如果有任何错误)...
  • 我刚刚用 getChatData 更新了我的问题。如您所见,它是异步的,但我只是添加它以尝试停止错误。我现在要测试你的代码。
  • @Ben Owen,我检查并重构了你的其他代码,试试这个,如果你有任何错误,那么它是XY problem,如果你有其他问题,请随时通知我们
猜你喜欢
  • 2020-03-01
  • 2019-05-27
  • 1970-01-01
  • 2019-04-01
  • 2021-02-15
  • 2018-01-11
  • 2020-12-19
  • 2021-08-17
  • 1970-01-01
相关资源
最近更新 更多