speechSynthesis.getVoices() returns empty array on Windows [duplicate]
Asked Answered
L

1

27

I am making a Chrome Extension, in which I am using Speech Synthesis. When I type speechSynthesis.getVoices() in the console I get an Array of 21 different voices. Great!

When I console.log() the same line within my javascript code, I get an Empty Array in the console. What's the matter, I can't figure out!

Lineation answered 27/3, 2018 at 7:25 Comment(6)
Could the internal array that speechSynthesis gets its data from be populated asynchronously? What happens when you try to run your code after a setTimeout instead?Doughy
Yeah, you are right. It works with a timeout of 1 second for me. Thanks!Lineation
Is there a better way to do this with a promise? I tried the following but it still returns an empty array: function set_up_speech() { return new Promise(function(resolve, reject) { var synth = window.speechSynthesis; var voices = synth.getVoices(); resolve(voices) }) } CALLED WITH set_up_speech().then(function(voices) { console.log(voices) });Shaunta
@Shaunta It returns an empty array since you immediately resolved the promise and returned the array as it is without checking if the array voices is populated with the voice names and is not empty.Lineation
@MelvinAbraham , thanks, i ended up using this: stackoverflow.com/a/51998112Shaunta
@Shaunta I have posted my own version to the problem. Check it out...Lineation
L
32

As pointed out by @CertainPerformance in the comments, when a page is loaded, it takes some amount of time to populate the voices array as it does so, asynchronously. Due to which when the array is logged into the console immediately after the page loads, we see an empty array...

To fix this, we console log it after some time (say, 10 or 50 ms):

setTimeout(() => {
    console.log(window.speechSynthesis.getVoices());
}, <time_in_ms>);

If you want to achieve the same with Promises, then, here's the code:

function setSpeech() {
    return new Promise(
        function (resolve, reject) {
            let synth = window.speechSynthesis;
            let id;

            id = setInterval(() => {
                if (synth.getVoices().length !== 0) {
                    resolve(synth.getVoices());
                    clearInterval(id);
                }
            }, 10);
        }
    )
}

let s = setSpeech();
s.then((voices) => console.log(voices));    // Or any other actions you want to take...

Here, after each time interval, we check whether the voices array returned by getVoices() is empty or not. if it's not, we end up resolving the promise...

Lineation answered 24/8, 2018 at 13:14 Comment(5)
If you go for this approach, referencing speechSynthesis may be necessary before the browser gets to loading the voices (I suppose it doesn't do that unless deemed necessary). You'll likely want to go with the onvoiceschanged event.Weaken
I'd imagine there should be some sort of "voicesReady" or "speechSynthesisReady" event, maybe in the future, and you can listen on it, rather then to check on it every 10ms... which is quite brute-force.Kayekayla
yes! window.speechSynthesis.onvoiceschanged = () =>{ console.warn('voices are ready',window.speechSynthesis.getVoices()); };Spiniferous
It's better to use the onvoiceschanged event instead of setInterval as @Spiniferous describes. See also #21514206Kamerad
Why let s = setSpeech() first? why not just setSpeech().then((voices) =>Zeidman

© 2022 - 2024 — McMap. All rights reserved.