speechsynthesis api 的 `getvoices()` 方法在页面加载初期常返回空数组,因其语音列表异步加载;需监听 `voiceschanged` 事件确保语音就绪后再获取并设置 voice。
SpeechSy

✅ 正确做法是:始终通过 speechSynthesis.onvoiceschanged 事件(或 addEventListener("voiceschanged", ...))监听语音就绪时机。该事件会在语音列表首次加载完成或后续动态更新(如系统新增语音)时触发,是唯一可靠的同步点。
以下是推荐的健壮实现方式:
function speakWithCustomVoice() {
const msg = new SpeechSynthesisUtterance("Hello World!");
// 确保语音已加载
function setAndSpeak() {
const voices = speechSynthesis.getVoices();
// 安全检查:避免越界访问
if (voices.length > 2) {
msg.voice = voices[2]; // 例如选择第3个语音(如 Microsoft Linda)
speechSynthesis.speak(msg);
} else {
console.warn("未检测到足够多的语音,使用默认语音播放");
speechSynthesis.speak(msg);
}
}
// 首次监听 voiceschanged
speechSynthesis.addEventListener("voiceschanged", setAndSpeak, { once: true });
// 兜底处理:若事件未触发(极罕见),延迟重试(可选)
setTimeout(() => {
if (speechSynthesis.getVoices().length === 0) {
console.warn("警告:voiceschanged 事件未触发,尝试手动重试");
setAndSpeak();
}
}, 1000);
}
speakWithCustomVoice();⚠️ 注意事项:
- 不要依赖 setTimeout 替代事件监听:setTimeout(…, 100) 是不可靠的“魔法数字”,不同设备/网络下语音加载时间差异大;
- once: true 选项防止重复绑定:避免多次触发导致重复播报;
- 始终校验 voices.length:索引访问前必须判断数组长度,否则可能报 Cannot set property 'voice' of undefined;
- 跨浏览器兼容性:Firefox、Chrome、Edge 均支持 voiceschanged,但 Safari(iOS/macOS)对 Web Speech API 支持有限(仅部分版本支持且需用户授权),生产环境建议添加特性检测与降级提示。
总结:getVoices() 的“空数组陷阱”本质是异步资源加载时序问题。拥抱事件驱动模型,以 voiceschanged 为守门人,即可稳定、可维护地控制语音合成行为。








