HTTP与WS调用后如何返回实际数据而非Promise?问题排查
问题分析与解决方案
首先得明确一个核心点:JavaScript里的异步操作(比如HTTP请求、WebSocket消息)的结果,没办法同步拿到——你必须等待异步操作完成,才能获取到实际数据。你看到的Promise<Pending>就是因为代码还没等异步流程走完就去拿结果了,而且你的代码里还有两个关键错误:
你的代码里的问题
- WebSocket消息收集时机错误:你在
onopen回调里直接返回messages数组,但这时候WebSocket还没开始接收消息呢!onmessage是异步触发的,当你return messages的时候,它还是空数组,只有后续收到消息才会往里面push数据,但这时候你的wsRequest这个Promise已经resolve成空数组了。 - 错误地认为
then能直接返回同步值:wsRequest.then(res => res)返回的依然是Promise,因为then本身就是异步方法,你同步打印data的时候,异步流程还没完成,所以看到的是pending状态的Promise。
修复方案
我们需要把WebSocket的消息收集过程包装成一个Promise,这样就能在消息接收完成后再resolve出最终的数组。这里假设你要收集100条数据(对应你的SQL LIMIT 100),或者等服务器发送完数据后关闭WebSocket,两种情况的处理方式如下:
方案1:用Promise包装WebSocket流程,收集指定数量的消息
const authenticationRequest = () => axios.post(authenticationUrl, { user: username, password }) .then((response) => response.data) .catch((error) => console.error('Error Response', error)); // 把WebSocket请求包装成返回Promise的函数 const getWsMessages = async (token) => { return new Promise((resolve, reject) => { const webSocketRequest = new WebSocket(topicDataUrl); const messages = []; const expectedCount = 100; // 对应SQL语句的LIMIT 100 webSocketRequest.onopen = () => { const firstMessage = { token: token, stats: 2, sql: "SELECT * FROM cc_payments LIMIT 100", live: false }; webSocketRequest.send(JSON.stringify(firstMessage)); }; webSocketRequest.onmessage = (streamEvent) => { const message = JSON.parse(streamEvent.data); if (message.type === 'RECORD') { messages.push(message.data.value); // 收集到指定数量的消息后,resolve并关闭WebSocket if (messages.length === expectedCount) { webSocketRequest.close(); resolve(messages); } } }; // 处理WebSocket错误 webSocketRequest.onerror = (error) => { reject(error); webSocketRequest.close(); }; // 万一服务器提前关闭连接,返回已收集的消息 webSocketRequest.onclose = () => { if (messages.length > 0) { resolve(messages); } else { reject(new Error('WebSocket closed without receiving messages')); } }; }); }; // 用async/await简化异步流程 const fetchData = async () => { try { const reqToken = await authenticationRequest(); const data = await getWsMessages(reqToken); console.log(data, 'DATA'); // 这里就能拿到实际的100条数据数组了 return data; } catch (error) { console.error('Fetch failed:', error); } }; // 启动整个异步流程 fetchData();
方案2:监听WebSocket关闭事件(若服务器发送完数据主动关闭连接)
如果服务器在发送完所有数据后会主动关闭WebSocket,你可以去掉onmessage里的数量判断,直接在onclose事件中resolve已收集的消息:
// 修改getWsMessages里的onmessage和onclose部分 webSocketRequest.onmessage = (streamEvent) => { const message = JSON.parse(streamEvent.data); if (message.type === 'RECORD') { messages.push(message.data.value); } }; webSocketRequest.onclose = () => { if (messages.length === 0) { reject(new Error('No messages received before connection closed')); } else { resolve(messages); } };
关键知识点回顾
- 所有异步操作(axios、WebSocket)的结果都只能通过**Promise的
then/catch或者async/await**来获取,没法同步拿到。 - 当你需要等待多个异步步骤(先拿token,再连WebSocket收消息),必须把这些步骤串联成异步流程,不能在异步操作还没完成的时候就返回结果。
- WebSocket的
onmessage是事件驱动的,必须用Promise来封装这个事件流,才能把异步的消息收集转换成可以等待的Promise。
内容的提问来源于stack exchange,提问作者user10104341
相关产品推荐
相关产品推荐

