NodeJS中如何监听qrstate变量变化并向前端浏览器返回响应
你当前的代码问题在于/getqrstate接口仅在qrstate非空时返回响应,若请求时qrstate还未被回调赋值,前端请求会一直处于挂起状态直到超时,也无法感知后续qrstate的更新。以下是3种可落地的解决方案:
方案1:前端短轮询(改造成本最低,适合小流量场景)
直接改造/getqrstate接口,无论qrstate是否有值都直接返回结果,前端定时重复请求直到拿到成功状态。
后端代码改造
var qrstate = "" app.get('/getqrstate', function (req, res) { res.jsonp({ status: qrstate ? 'success' : 'pending', msg: qrstate ? 'kiosk state is found' : 'waiting for scan', data: qrstate }) }) app.get('/callback', function (req, res) { qrstate = req.query.state // 必须给第三方回调返回响应,避免服务商重复发起回调 res.send('ok') })
前端逻辑
页面加载后每隔1-2秒调用一次/getqrstate接口,直到返回status: success后停止轮询,执行后续业务逻辑。
- 优点:逻辑简单无额外依赖,兼容所有浏览器
- 缺点:状态更新有最高1-2秒的延迟,高并发下请求量较大
方案2:后端长轮询(实时性更高,适合中等流量场景)
后端收到/getqrstate请求后,如果qrstate为空就暂存请求不返回,等到qrstate更新时再响应所有暂存的请求,超时未更新则返回待重试状态。
后端代码改造
var qrstate = "" // 存储所有挂起的/getqrstate请求响应对象 const pendingResponses = [] app.get('/getqrstate', function (req, res) { // 已有状态直接返回 if (qrstate) { return res.jsonp({ status: 'success', msg: 'kiosk state is found', data: qrstate }) } // 无状态则挂起请求,设置30秒超时 const timeout = setTimeout(() => { const idx = pendingResponses.indexOf(res) if (idx > -1) pendingResponses.splice(idx, 1) res.jsonp({ status: 'pending', msg: 'timeout, please retry' }) }, 30000) res.timeoutId = timeout pendingResponses.push(res) }) app.get('/callback', function (req, res) { qrstate = req.query.state // 通知所有挂起的请求返回结果 pendingResponses.forEach(resItem => { clearTimeout(resItem.timeoutId) resItem.jsonp({ status: 'success', msg: 'kiosk state is found', data: qrstate }) }) // 清空待响应列表 pendingResponses.length = 0 res.send('ok') })
前端逻辑
每次收到pending状态后自动重新发起请求即可,无需固定间隔轮询。
- 优点:实时性高,几乎无延迟,请求量远低于短轮询
- 缺点:多实例部署时需要配合Redis发布订阅实现跨实例请求通知,服务器重启会导致挂起的请求丢失
方案3:WebSocket双向推送(适合需要大量实时交互的场景)
后端集成WebSocket服务,前端和后端建立长连接,callback接口收到状态后直接通过WebSocket推送给对应前端,无需前端主动请求。
后端代码改造
首先安装依赖:npm install ws
var qrstate = "" const WebSocket = require('ws') // 启动WebSocket服务,端口可自行调整 const wss = new WebSocket.Server({ port: 8080 }) // 存储所有已连接的WebSocket客户端 const wsClients = [] wss.on('connection', (ws) => { wsClients.push(ws) // 连接断开时清理 ws.on('close', () => { const idx = wsClients.indexOf(ws) if (idx > -1) wsClients.splice(idx, 1) }) }) // callback接口直接推送消息 app.get('/callback', function (req, res) { qrstate = req.query.state // 推送状态给所有前端 wsClients.forEach(ws => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ status: 'success', msg: 'kiosk state is found', data: qrstate })) } }) res.send('ok') })
前端逻辑
页面加载时建立WebSocket连接,监听消息事件即可,无需主动调用/getqrstate接口。
- 优点:实时性最高,支持双向通信,后续扩展其他实时功能成本低
- 缺点:需要处理WebSocket断线重连逻辑,多实例部署同样需要跨实例消息同步
通用注意事项
- 多实例部署场景下,内存存储的
qrstate和待通知列表各实例独立,需要改用Redis存储qrstate,配合Redis发布订阅实现跨实例的状态通知 - 要补充
qrstate的重置逻辑,比如二维码过期、前端拿到结果后清空qrstate,避免上次的状态影响下一次扫码流程
内容的提问来源于stack exchange,提问作者Daryl Wong
相关产品推荐
相关产品推荐

