You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 02:30:05