在Chrome浏览器(v72,W10)和Opera中,以下代码段偶尔 does not seem to run end
的{{1}}侦听器,可能是{片段运行的50倍。 (很抱歉,在此版本的原始版本中,可以更轻松地复制它-现在,创建按钮单击时的发音使该错误更加罕见了)
SpeechSynthesisUtterance
button.onclick = () => {
console.log('start script');
button.disabled = true;
const utt = new SpeechSynthesisUtterance('e');
utt.addEventListener('end', () => {
console.log('end event triggered');
});
// just for debugging completeness, no errors seem to be thrown though
utt.addEventListener('error', (err) => {
console.log('err', err)
});
speechSynthesis.speak(utt);
setTimeout(() => {
console.log('finished?');
}, 1500);
};
从我所看到的情况来看,如果<button id="button">click</button>
事件曾经被激活,它将在给定的页面加载范围内始终激活,这就是为什么我禁用了以上代码段中的按钮。 (您必须多次重新运行该代码段才能看到问题)
如果在禁用自动播放限制的情况下在Chrome中运行以下代码段(W10为72),则可以更轻松地重现该代码段。 (转到end
,将自动播放策略更改为不需要用户手势)。
(不幸的是,在Opera中,与第一个片段类似,复制起来似乎很困难)
chrome://flags/
据我所知,Firefox(56)并没有出现此问题-在其中,console.log('start script');
function say(text) {
const utt = new SpeechSynthesisUtterance(text);
utt.addEventListener('end', () => console.log('end: ' + text));
// just for debugging completeness, no errors seem to be thrown though
utt.addEventListener('error', (err) => {
console.log('err on ' + text + ', ', err)
});
speechSynthesis.speak(utt);
}
say('foo');
say('bar');
侦听器始终可以正常触发。
我是否以某种方式不能正确地附加侦听器,或者这是Chromium的错误?
答案 0 :(得分:5)
编辑/更新 :@Ouroborus指出这确实是open Chromium bug
我启动了Sawbuck,并开始尝试重现此错误。发生问题时,我始终看到在“开始脚本”和“完成?”之间发生gc活动。日志。
成功的例子:
失败示例:
因此,似乎gc进程正在干扰正在传递的end
事件。
为了进一步验证这一理论,我从--js-flags="--expose-gc"
标志开始chrome,该标志启用了v8 gc功能,允许强制垃圾收集。
如果我修改了测试代码并在window.gc()
之前添加了console.log('start script')
,我将无法重现该问题(尝试次数超过50次)。这可能是因为它减少/消除了语音发声期间出现gc的机会。
似乎您may be able to prevent SpeechSynthesisUtterance
对console.log
对象进行了gc操作。这似乎确实导致事件的一致交付。显然,如果要创建大量这些对象,阻止它们的收集可能不是理想的选择:
button.onclick = () => {
console.log('start script');
button.disabled = true;
const utt = new SpeechSynthesisUtterance('e');
// Prevent garbage collection of utt object
console.log(utt);
utt.addEventListener('end', () => {
console.log('end event triggered');
});
// just for debugging completeness, no errors seem to be thrown though
utt.addEventListener('error', (err) => {
console.log('err', err)
});
speechSynthesis.speak(utt);
setTimeout(() => {
console.log('finished?');
}, 1500);
};
<button id="button">click</button>