使用WebSocket加try/catch仍出现Uncaught (in promise)错误求助
问题描述
我通过WebSocket API获取数据,收到消息后会检查是否存在错误,若有错误则拒绝Promise请求。但拒绝请求后,控制台仍出现Uncaught (in promise)错误,即便已用try/catch包裹所有代码。相关代码如下:
Service类
class Service { static getUser() { return new Promise((resolve, reject) => { // 试过用try/catch包裹整个函数体,但问题依旧 const messageReceiveCallback = (message) => { try { const data = JSON.parse(message.data); if (data.hasError) { // 错误发生在这里 reject(data); } else { resolve(data); } } catch (error) { // 错误没有走到这里 reject(error); } finally { derivService.socketConnection.removeEventListener( "message", messageReceiveCallback ); } }; socketConnection.addEventListener( "message", messageReceiveCallback ); publicApi.getUser(apiPayload); }) } }
React组件
const retrieveUser = async () => { try { await Service.getUser(payload); // 不加await会看到两次错误,加了只看到一次 } catch (e: any) { console.log("eee", e); } }; useEffect(() => { retrieveUser(); }, []);
问题根源
问题出在Promise状态只能被修改一次:当WebSocket的message事件被多次触发时(比如服务器返回多条消息、事件重复触发),第一次调用reject后,Promise已经进入rejected终态,后续再调用reject或resolve会直接抛出未捕获错误——这个错误无法被外层try/catch捕获,因为它是在Promise状态确定后执行的无效状态修改操作。
另外,虽然你在finally里移除了事件监听,但如果第一次回调执行完成前,已有多个message事件被放入事件队列,回调还是会被多次执行,导致重复修改Promise状态。
修复方案
在Promise的executor函数里加一个状态标志,确保resolve/reject只会被调用一次:
class Service { static getUser() { return new Promise((resolve, reject) => { let isHandled = false; // 标记是否已处理过Promise状态 const messageReceiveCallback = (message) => { if (isHandled) return; // 已处理过直接返回,避免重复操作 try { const data = JSON.parse(message.data); isHandled = true; // 先标记为已处理,再修改状态 if (data.hasError) { reject(data); } else { resolve(data); } } catch (error) { isHandled = true; reject(error); } finally { derivService.socketConnection.removeEventListener( "message", messageReceiveCallback ); } }; socketConnection.addEventListener("message", messageReceiveCallback); publicApi.getUser(apiPayload); }) } }
额外优化
可以给WebSocket请求加超时处理,避免因网络问题导致Promise一直处于pending:
static getUser() { return new Promise((resolve, reject) => { let isHandled = false; // 设置10秒超时 const timeoutTimer = setTimeout(() => { if (!isHandled) { isHandled = true; reject(new Error("请求超时,请检查网络")); socketConnection.removeEventListener("message", messageReceiveCallback); } }, 10000); const messageReceiveCallback = (message) => { if (isHandled) return; clearTimeout(timeoutTimer); // 收到消息后清除超时定时器 try { const data = JSON.parse(message.data); isHandled = true; if (data.hasError) { reject(data); } else { resolve(data); } } catch (error) { isHandled = true; reject(error); } finally { socketConnection.removeEventListener("message", messageReceiveCallback); } }; socketConnection.addEventListener("message", messageReceiveCallback); publicApi.getUser(apiPayload); }) }
内容的提问来源于stack exchange,提问作者Amit
相关产品推荐
相关产品推荐

