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 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>
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 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) 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?
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 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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With