问题的根因不是 JS 异步而是 flv.js 的 WebSocket 连接一旦发起底层资源释放不及时坏流一直占着浏览器连接槽 。换什么库、什么信号量都打不到这个层面。换方案用 mpegts.jsflv.js 的现代维护分支加 WebSocket 预探测。先用最快的 WebSocket 测每个 URL 是否可达死的直接跳过不给它碰 flvjs活的一个一个串行启动。这样坏流连 WebSocket 都不碰100% 不挡。!DOCTYPE html html langzh-CN head meta charsetUTF-8 title多路 WS-FLV 监控/title style html, body { margin: 0; background: #111; color: #fff; font-family: Arial; } h2 { text-align: center; padding: 10px; } .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(480px, 1fr)); gap: 12px; padding: 12px; } .player { background: #000; position: relative; } .title { position: absolute; top: 6px; left: 8px; font-size: 14px; background: rgba(0,0,0,.6); padding: 2px 6px; border-radius: 4px; } .status { position: absolute; top: 6px; right: 8px; font-size: 11px; padding: 2px 6px; border-radius: 4px; } .status.wait { background: #555; } .status.ok { background: #080; } .status.bad { background: #800; } video { width: 100%; height: 360px; background: #000; } /style /head body h2多路 WS-FLV 实时监控/h2 div idapp classgrid/div script srchttps://cdn.jsdelivr.net/npm/mpegts.js1.7.3/dist/mpegts.js/script script var cameras [ { id: dataHubRightVideo_1_2_0, title: 船1, url: ws://10.111.222.5:8000/live/cam1.live.flv }, { id: dataHubRightVideo_1_2_1, title: 船2, url: ws://10.111.222.5:8000/live/cam1.live.flv }, { id: dataHubRightVideo_1_2_2, title: 船3, url: ws://10.111.222.5:8000/live/cam1.live.flv }, { id: dataHubRightVideo_1_2_3, title: 船4, url: ws://10.111.222.5:8000/live/cam1.live.flv }, { id: dataHubRightVideo_1_2_4, title: 船5, url: ws://10.111.222.5:8000/live/cam1.live.flv }, { id: dataHubRightVideo_1_2_5, title: 船6, url: ws://10.111.222.5:8000/live/cam1.live.flv }, { id: dataHubRightVideo_1_2_6, title: 船7, url: ws://10.111.222.5:8000/live/cam1.live.flv }, { id: dataHubRightVideo_1_2_7, title: 船8, url: ws://10.111.222.5:8000/live/cam1.live.flv }, { id: dataHubRightVideo_1_2_8, title: 船9, url: ws://10.111.222.5:8000/live/cam1.live.flv }, { id: dataHubRightVideo_1_2_9, title: T5楼顶船10, url: ws://10.111.222.5:8000/live/cam1.live.flv }, ] var app document.getElementById(app) var slots [] // 第一步立即渲染所有 DOM cameras.forEach(function (cam) { var box document.createElement(div) box.className player var title document.createElement(div) title.className title title.innerText cam.title box.appendChild(title) var status document.createElement(div) status.className status wait status.innerText 等待 box.appendChild(status) var video document.createElement(video) video.controls true video.muted true video.autoplay true video.style.background #222 box.appendChild(video) app.appendChild(box) slots.push({ cam: cam, video: video, status: status, player: null }) }) // 第二步WebSocket 预探测逐个来互不阻塞 var currentIndex 0 function probeNext() { if (currentIndex slots.length) return var slot slots[currentIndex] currentIndex slot.status.className status wait slot.status.innerText 探测 var ws null var probed false function finishProbe(alive) { if (probed) return probed true if (ws) { try { ws.close() } catch (_) { } ws null } if (alive) { slot.status.className status ok slot.status.innerText 播放 startPlayer(slot) } else { slot.status.className status bad slot.status.innerText 不可达 slot.video.style.background #400 } // 立即探测下一路 probeNext() } try { ws new WebSocket(slot.cam.url) ws.binaryType arraybuffer ws.onopen function () { // 收到数据才算活着 } ws.onmessage function () { // 收到任何数据 活着 finishProbe(true) } ws.onerror function () { finishProbe(false) } ws.onclose function () { finishProbe(false) } // 1 秒超时 setTimeout(function () { finishProbe(false) }, 1000) } catch (e) { finishProbe(false) } } function startPlayer(slot) { var player mpegts.createPlayer({ type: flv, url: slot.cam.url, isLive: true, enableWorker: false, lazyLoad: false, stashInitialSize: 128 }) player.attachMediaElement(slot.video) slot.player player player.on(mpegts.Events.ERROR, function () { slot.player null try { player.destroy() } catch (_) { } slot.status.className status ok slot.status.innerText 重连 // 3 秒后重连 setTimeout(function () { startPlayer(slot) }, 3000) }) player.load() player.play().catch(function () { }) } // 串行探测启动 probeNext() // 同时启动多路探测一次发 2 路 setTimeout(probeNext, 200) // 第三步15 秒后对失败的重试 setInterval(function () { slots.forEach(function (slot) { if (slot.status.className.indexOf(bad) 0 || slot.status.innerText 重连) { // 重探测 slot.status.className status wait slot.status.innerText 重试 var ws null var probed false function finish(alive) { if (probed) return probed true if (ws) { try { ws.close() } catch (_) { } ws null } if (alive !slot.player) { slot.status.className status ok slot.status.innerText 播放 startPlayer(slot) } else if (!alive) { slot.status.className status bad slot.status.innerText 不可达 } } try { ws new WebSocket(slot.cam.url) ws.binaryType arraybuffer ws.onmessage function () { finish(true) } ws.onerror function () { finish(false) } ws.onclose function () { finish(false) } setTimeout(function () { finish(false) }, 1000) } catch (e) { finish(false) } } }) }, 15000) window.addEventListener(beforeunload, function () { slots.forEach(function (s) { if (s.player) { try { s.player.destroy() } catch (_) { } } }) }) /script /body /html