最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why does a SpeechSynthesisUtterance sometimes not fire an 'end' event in Chromium-based browsers? -

programmeradmin1浏览0评论

In both Chrome (v72, W10) and Opera, the following snippet very occasionally does not seem to run the attached end listener to the SpeechSynthesisUtterance, maybe 1 out of 50 times the snippet is run. (Sorry, in the original version of this, it could be reproduced much more easily - now, creating the utterance on button click looks to have made the bug much more rare)

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 pleteness, 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>

In both Chrome (v72, W10) and Opera, the following snippet very occasionally does not seem to run the attached end listener to the SpeechSynthesisUtterance, maybe 1 out of 50 times the snippet is run. (Sorry, in the original version of this, it could be reproduced much more easily - now, creating the utterance on button click looks to have made the bug much more rare)

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 pleteness, 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>

From what I've seen, if the end event ever activates, it will always activate within a given pageload, which is why I disable the button in the above snippet. (you'll have to rerun the snippet many times to see the problem)

You can reproduce it more readily if you run the below snippet in Chrome (72 on W10) with autoplay restrictions disabled. (go to chrome://flags/, change Autoplay policy to No user gesture is required).

(In Opera, it seems to be similarly difficult to reproduce as in the first snippet, unfortunately)

console.log('start script');
function say(text) {
  const utt = new SpeechSynthesisUtterance(text);
  utt.addEventListener('end', () => console.log('end: ' + text));
  
  // just for debugging pleteness, 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) does not have this issue as far as I can see - in it, the end listener always fires properly.

Am I somehow not attaching the listener sufficiently properly, or is this a Chromium bug?

Share Improve this question edited Mar 8, 2019 at 6:12 Snow asked Feb 25, 2019 at 7:04 SnowSnow 4,1073 gold badges16 silver badges43 bronze badges 5
  • Oops! It looks like Opera hasn't implemented the autoplay restrictions yet, and I had a custom setting on my Chrome that permitted autoplay, so I didn't see the error. Triggering the utterance on click still results in the bug, though unfortunately it's a lot less reproducible. – Snow Commented Mar 8, 2019 at 5:06
  • didn't you use "onend" instead of "utt.addEventListener('end', () => {" – Udara Kasun Commented Mar 8, 2019 at 6:24
  • @UdaraKasun No, I always used addEventListener for the end event, you can see the edit history, but I tried it and it looks like using .onend = behaves the same. – Snow Commented Mar 8, 2019 at 6:35
  • Able to reproduce in Chrome 72.0.3626.81. Maybe 1 in 20 or so and doesn't seem to happen when the dev console is open. – Ouroborus Commented Mar 8, 2019 at 8:38
  • @Kaiido reproduced on Mojave, chrome v 72.0.3626.121 – WilliamNHarvey Commented Mar 8, 2019 at 19:15
Add a ment  | 

1 Answer 1

Reset to default 8 +500

Edit/Update: @Ouroborus pointed out that this is indeed an open Chromium bug


I fired up Sawbuck and started attempts to reproduce this. When the issue occurs, I consistently see gc activity happening between the 'start script' and 'finished?' logs.

Example of success:

Example of failure:

So, it seems like it may be that the gc process is interfering with the end event being delivered.

To further test this theory I started chrome with the --js-flags="--expose-gc" flag which enables the v8 gc function, allowing one to force garbage collection.

If I modify your test code and add window.gc() before console.log('start script'), I can no longer reproduce the issue (>50 attempts). It's possible this is because it reduces/eliminates the chance that gc occurs during the speech utterance.

It seems that you may be able to prevent the SpeechSynthesisUtterance object from being gc'd by console.log-ing it. This does seem to result in consistent delivery of the event. Obviously preventing their collection is probably not ideal if you're creating a great number of these objects:

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 pleteness, 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>

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论