WebSocket确认重连逻辑中Promise无法解析的问题
我完全懂你遇到的坑——当WebSocket服务器一开始离线,需要多次重试才能连上时,你的myConnect()返回的Promise就一直卡着不resolve,对吧?这本质上是因为你的重试逻辑没有把后续的连接尝试和初始Promise绑定在一起,导致第一个Promise一直处于pending状态,永远等不到resolve的信号。
先看看常见的错误写法(就是你可能踩的坑)
比如这种写法,第一次连接失败后,只是重新调用了myConnect(),但初始Promise的resolve/reject根本没被触发:
function myConnect() { return new Promise((resolve, reject) => { const ws = new WebSocket('ws://your-server'); ws.onopen = () => resolve(ws); ws.onerror = () => { // 这里只是新建了一个Promise,但和初始的Promise毫无关系! myConnect(); }; }); }
这种情况下,第一次连接失败后,初始Promise就被彻底遗忘了,后续的重试结果永远传不回去,自然一直pending。
正确的实现方式:把重试逻辑整合进Promise链
这里提供两种直观的写法,任选其一就行:
方法1:用递归+Promise链式调用
function myConnect() { // 封装单次连接尝试 const tryConnect = () => { return new Promise((resolve, reject) => { const ws = new WebSocket('ws://your-server'); ws.onopen = () => { console.log('WebSocket连接成功!'); resolve(ws); }; ws.onerror = (err) => { console.error('连接失败,准备重试:', err); reject(err); // 触发catch,进入重试流程 }; ws.onclose = () => { console.log('连接意外关闭,准备重试'); reject(new Error('连接已关闭')); }; }); }; // 递归实现重试逻辑 const retryConnect = () => { return tryConnect() .catch(() => { // 延迟1秒再重试,避免频繁请求压垮服务器 return new Promise(resolve => setTimeout(resolve, 1000)) .then(retryConnect); // 递归调用,直到成功 }); }; return retryConnect(); }
这个写法里,所有的重试都通过Promise链串联起来,最终连接成功时,整个链的顶端(也就是myConnect()返回的Promise)会被resolve,完美解决pending的问题。
方法2:用async/await+循环(更直观)
如果你觉得递归绕脑子,用async函数配合无限循环会更易懂:
async function myConnect() { while (true) { try { // 单次连接尝试 const ws = await new Promise((resolve, reject) => { const socket = new WebSocket('ws://your-server'); socket.onopen = () => resolve(socket); socket.onerror = (err) => reject(err); socket.onclose = () => reject(new Error('连接关闭')); }); console.log('WebSocket连接成功!'); return ws; // 成功后直接返回,终止循环 } catch (err) { console.error('连接失败,1秒后重试:', err); await new Promise(resolve => setTimeout(resolve, 1000)); } } }
这里用while(true)一直尝试连接,失败就等1秒再试,直到成功后return,async函数返回的Promise就会自动resolve这个WebSocket实例。
可选优化:添加最大重试次数
如果不想无限重试,可以加个最大次数限制:
async function myConnect(maxRetries = 10) { let retryCount = 0; while (retryCount < maxRetries) { try { const ws = await new Promise((resolve, reject) => { const socket = new WebSocket('ws://your-server'); socket.onopen = () => resolve(socket); socket.onerror = (err) => reject(err); socket.onclose = () => reject(new Error('连接关闭')); }); console.log('WebSocket连接成功!'); return ws; } catch (err) { retryCount++; console.error(`第${retryCount}次重试失败,1秒后继续...`, err); await new Promise(resolve => setTimeout(resolve, 1000)); } } // 超过最大次数就抛出错误 throw new Error(`已尝试${maxRetries}次连接,全部失败`); }
核心逻辑总结
不管哪种写法,关键都是确保所有重试操作的最终结果能传递给初始的Promise——要么通过Promise链的递归,要么通过async函数的return,这样当服务器最终可用时,myConnect()的Promise就能正常resolve,不会再卡住啦。
内容的提问来源于stack exchange,提问作者ccorry

