尝试从异步函数调用外部函数时未处理的承诺拒绝

时间:2021-05-29 13:28:50

标签: react-native es6-promise

错误信息:

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' 函数。我猜这是路线原因。我认为解决方案将是它的一些异步版本......

1 个答案:

答案 0 :(得分:2)

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

我稍微修改了您的代码,如果您收到来自 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 关键字使每个异步操作同步即可。