问题描述
在Chrome(v72,W10)和Opera中,以下代码段偶尔 附加的end
侦听器到SpeechSynthesisUtterance
,可能是运行代码段50次中的1次。
(对不起,在这个版本的原始版本中,它可以更容易地复制 - 现在,创建按钮点击的话语看起来让错误变得更加罕见)
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>
从我所看到的,如果end
事件激活,它将始终在给定的页面加载中激活,这就是我禁用上述代码段中的按钮的原因。
(你必须多次重新运行该片段以查看问题)
如果您在Chrome中运行以下代码段(W10上为72)且禁用了自动播放限制,则可以更轻松地重现它。
(转到chrome://flags/
,将自动播放策略更改为无需用户手势 )。
(在Opera中,不幸的是,在第一个片段中似乎很难再现)
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');
就我所见,Firefox(56)没有这个问题 - 在其中, end
监听器总是正常激活。
我是不是在某种程度上没有正确地附加听众,或者这是一个Chromium bug?
1楼
cody
5
已采纳
2019-03-08 16:38:28
编辑/更新 :@Ouroborus指出这确实是一个
我解雇了并开始尝试重现这一点。 当问题发生时,我一直看到在'开始脚本'和'完成'之间发生了gc活动? 日志。
成功案例:
失败的例子:
因此,似乎gc进程可能会干扰正在传递的end
事件。
为了进一步测试这个理论,我用--js-flags="--expose-gc"
标志启动了chrome,它启用了v8 gc函数,允许强制垃圾收集。
如果我修改你的测试代码并在console.log('start script')
window.gc()
之前添加window.gc()
,我将无法再重现该问题(> 50次尝试)。
这可能是因为它减少/消除了语音发声过程中gc发生的可能性。
看来你的SpeechSynthesisUtterance
对象由被gc'd console.log
-ing它。
这似乎确实导致事件的一致传递。
如果您正在创建大量这些对象,显然阻止它们的收集可能并不理想:
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>