为什么有时在基于Chromium的浏览器中,SpeechSynthesisUtterance不会触发“结束”事件?

时间:2019-02-25 07:04:06

标签: javascript speech-synthesis

在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的错误?

1 个答案:

答案 0 :(得分:5)

编辑/更新 :@Ouroborus指出这确实是open Chromium bug


我启动了Sawbuck,并开始尝试重现此错误。发生问题时,我始终看到在“开始脚本”和“完成?”之间发生gc活动。日志。

成功的例子:

enter image description here

失败示例:

enter image description here

因此,似乎gc进程正在干扰正在传递的end事件。

为了进一步验证这一理论,我从--js-flags="--expose-gc"标志开始chrome,该标志启用了v8 gc功能,允许强制垃圾收集。

如果我修改了测试代码并在window.gc()之前添加了console.log('start script'),我将无法重现该问题(尝试次数超过50次)。这可能是因为它减少/消除了语音发声期间出现gc的机会。

似乎您may be able to prevent SpeechSynthesisUtteranceconsole.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>