promise 可封装视频加载事件实现异步控制,最常用的是包装 loadedmetadata 事件等待元数据就绪;对 hls/dash 需借助 hls.js 等库提供 promise api;还可按需封装 loadeddata、canplay 等多阶段等待逻辑,并注意自动播放限制、src 设置时机及事件清理。

Promise 本身不直接处理视频流加载,但它常被用来封装视频元素的加载过程,让异步操作更可控、可链式调用。视频流初始化加载(比如 <video></video> 的 src 设置、load() 触发、元数据就绪、首帧渲染等)本质上是一系列事件驱动的过程,可用 Promise 包装关键节点,实现“等待视频准备好”的语义。
用 Promise 封装 video 元素的 loadedmetadata 事件
这是最常用的方式:等待视频元数据(时长、尺寸、码率等)加载完成,代表视频已能获取基本信息,适合做后续控制逻辑(如设置播放时间、显示 UI)。
示例代码:
function waitForVideoMetadata(videoEl) {
return new Promise((resolve, reject) => {
if (videoEl.readyState >= HTMLMediaElement.HAVE_METADATA) {
resolve(videoEl);
return;
}
const onLoadedMetadata = () => {
videoEl.removeEventListener('loadedmetadata', onLoadedMetadata);
resolve(videoEl);
};
const onError = () => {
videoEl.removeEventListener('loadedmetadata', onLoadedMetadata);
reject(new Error('Failed to load video metadata'));
};
videoEl.addEventListener('loadedmetadata', onLoadedMetadata);
videoEl.addEventListener('error', onError);
// 防止因网络或资源问题长期挂起
setTimeout(() => {
videoEl.removeEventListener('loadedmetadata', onLoadedMetadata);
videoEl.removeEventListener('error', onError);
reject(new Error('Timeout waiting for video metadata'));
}, 10000);
});
}
// 使用
const video = document.querySelector('#myVideo');
video.src = 'https://example.com/stream.mp4';
waitForVideoMetadata(video)
.then(v => {
console.log('视频元数据已就绪,时长:', v.duration);
// 可在此调用 play() 或设置 currentTime 等
})
.catch(err => console.error(err));
扩展支持流媒体(HLS/DASH)的初始化 Promise
原生 <video></video> 不直接支持 HLS(.m3u8)或 DASH(.mpd),需借助库(如 hls.js 或 video.js + VHS)。这些库通常提供基于 Promise 的加载 API。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
以 hls.js 为例:
import Hls from 'hls.js';
function initHlsStream(videoEl, src) {
return new Promise((resolve, reject) => {
if (!Hls.isSupported()) {
reject(new Error('HLS not supported'));
return;
}
const hls = new Hls();
hls.loadSource(src);
hls.attachMedia(videoEl);
hls.on(Hls.Events.MANIFEST_PARSED, () => {
resolve({ hls, video: videoEl });
});
hls.on(Hls.Events.ERROR, (event, data) => {
if (data.fatal) {
hls.destroy();
reject(new Error(`HLS error: ${data.reason}`));
}
});
});
}
// 使用
initHlsStream(video, 'https://example.com/stream.m3u8')
.then(({ hls, video }) => {
console.log('HLS 流已解析,可开始播放');
video.play().catch(e => console.warn('自动播放被阻止:', e));
});
组合多个加载阶段:从设置 src 到首帧渲染
若需确保视频不仅元数据就绪,还至少解码并渲染了第一帧(即视觉上“出现画面”),可结合 loadeddata 或 canplay 事件。注意事件触发顺序:loadstart → loadedmetadata → loadeddata → canplay → canplaythrough
推荐封装为可选阶段的 Promise 工厂:
function waitForVideoReady(videoEl, stage = 'canplay') {
const eventMap = {
metadata: 'loadedmetadata',
data: 'loadeddata',
play: 'canplay',
full: 'canplaythrough'
};
const eventName = eventMap[stage] || 'canplay';
return new Promise((resolve, reject) => {
const onEvent = () => {
videoEl.removeEventListener(eventName, onEvent);
resolve(videoEl);
};
const onError = () => {
videoEl.removeEventListener(eventName, onEvent);
reject(new Error(`Video failed to reach ${stage} stage`));
};
videoEl.addEventListener(eventName, onEvent);
videoEl.addEventListener('error', onError);
// 若已满足条件,立即 resolve
if (videoEl.readyState >= HTMLMediaElement[`HAVE_${stage.toUpperCase()}`] ||
(stage === 'metadata' && videoEl.readyState >= HTMLMediaElement.HAVE_METADATA)) {
resolve(videoEl);
return;
}
});
}
// 等待可播放(有足够缓冲)
waitForVideoReady(video, 'play').then(v => v.play());
注意事项与常见陷阱
-
自动播放策略限制:现代浏览器要求用户手势(如 click)后才能调用
play(),否则会抛出拒绝的 Promise。不要在 Promise resolve 后无条件play(),应结合用户交互。 -
src 设置时机:务必在创建 Promise 前设置好
video.src,否则事件不会触发;若使用动态 src,建议先设为空字符串再赋值,避免旧缓存干扰。 - 内存与事件清理:每次 Promise 封装都需手动移除事件监听器,否则可能造成重复触发或内存泄漏。
- 流媒体延迟:HLS/DASH 初始化比普通 MP4 更耗时,需合理设置超时(如 15–30 秒),并提供加载状态 UI。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










