You need to enable JavaScript to run this app. 使用fetch检测在线音频流状态返回200时卡住的解决方案咨询
优惠活动
大模型
产品
解决方案
定价
更多

使用fetch检测在线音频流状态返回200时卡住的解决方案咨询

问题原因分析

你遇到的fetch卡住问题,本质是因为在线音频流是持续传输的无限长响应,node-fetch默认会等待完整响应体接收完成才会resolve Promise,服务器会一直推送流数据,所以await永远不会返回。而且你要的只是校验流是否可用,不需要接收完整的流内容,所以不需要等待响应体加载。

另外你原有代码存在逻辑问题:第一个链接校验失败后直接return,不会遍历后续备用的流地址,下面的修复方案也同步解决了这个问题。

解决方案

方案1:仅校验响应头后立即终止请求

你不需要等待响应体返回,只要拿到200的响应头就可以判定流可用,拿到响应后直接销毁连接即可,修改后的代码如下:

export async function startBroadcast(this: Root) {
  if (!this.extensions.broadcast) {
    this.extensions['broadcast'] = this.client.voice?.createBroadcast()
    if (this.config.radio?.streamLink) {
      if (this.extensions.broadcast) {
        const broadcast = this.extensions.broadcast as VoiceBroadcast
        let tested: number = 0
        for (const link of this.config.radio.streamLink) {
          let req: Response | undefined;
          const controller = new AbortController();
          // 设置5秒超时兜底,避免网络异常卡住
          const timeout = setTimeout(() => controller.abort(), 5000);
          try {
            req = await this.fetch(link, { signal: controller.signal });
          } catch (e) {
            this.log(LoggingLevels.error, `${link} 请求超时或连接失败`);
            tested++
            if (tested == this.config.radio.streamLink.length) {
              this.log(
                LoggingLevels.emergency,
                `All stream links failed. Radio is likely offline`
              )
              throw new Error('All stream links failed')
            }
            // 校验失败继续试下一个链接
            continue;
          } finally {
            clearTimeout(timeout);
          }
          // 拿到响应头后直接销毁响应体,不等待音频内容传输
          if (req.body) req.body.destroy();
          if (!req.ok) {
            this.log(LoggingLevels.error, `${link} 不可用,状态码:${req.status}`)
            tested++
            if (tested == this.config.radio.streamLink.length) {
              this.log(
                LoggingLevels.emergency,
                `All stream links failed. Radio is likely offline`
              )
              throw new Error('All stream links failed')
            }
            continue
          }
          const broadcastOptions: StreamOptions = {
            highWaterMark: 50,
            volume: false
          }
          this.log(
            LoggingLevels.debug,
            `Starting up broadcast using stream link: ${link}`
          )

          broadcast.play(link, broadcastOptions)
          break
        }
      }
    }
  }
}

方案2:全局适配不同版本node-fetch的超时逻辑

  • 如果你用的是v2版本node-fetch,可以直接传timeout参数代替AbortController:this.fetch(link, { timeout: 5000 })
  • 如果你用的是v3及以上版本node-fetch,沿用上面代码的AbortController写法即可。
注意事项
  • 不要调用res.json()、res.text()、res.arrayBuffer()这类需要读取完整响应体的方法,否则还是会出现卡住问题
  • 只要拿到响应头的状态码就足够判断流是否可用,不需要接收实际的音频内容

内容的提问来源于stack exchange,提问作者Jonathan Stevens

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 10:15:04