Search code examples
javascriptpromiseasync-awaitspeech-recognitionannyang

How can I call Speech Recognition multiple times in 1 function?


I am relatively new to JavaScript and currently working a project a beginner probably shouldn't start on. I have been coding for some time in python and decided to give it a shot. I am trying to make a personal assistant, or as I like to call it, Jarvis (from IRON MAN). It currently can listen for commands and perform them just fine, however, I am trying to make a send email command that requires multiple input from a user all in one function. This send email function is technically in parseCommand() where I pass an identifier, known as i. I will eventually add more commands to parseCommand() and the identifier will help in knowing what command to perform. When I say, "jarvis send an email", parseCommand("email") is called. It is a promise because once it is done, I want to call annyang (command listener) again.

When "jarvis send an email" is triggered ->

"jarvis send an email" : async function(){
  annyang.abort(); // stop listening
  await parseCommand("email").then(annyang.start()); // Start listening for another command when done
}

parseCommand:

parseCommand(i){
    return new Promise(async (resolve) => {
        recognition.stop();



        if(i == "email"){
            var SUBJECT;
            var TEXT;
            var TO;

            console.log("Running: send an email")
            await this.promptListen("Who shall I send it to?").then( async (email) =>{
                TO = email;
                console.log("got TO", TO)

                await this.promptListen("What shall the subject be?").then( async (subject) => {
                    SUBJECT = subject;
                    console.log("got SUBJECT", SUBJECT)

                    await this.promptListen("What would you like to say?").then((text) => {
                        TEXT = text;
                        console.log("got TEXT", TEXT)
                        resolve()
                        })
                    })
                });
            }
        })
    }

promptListen command:

promptListen(prompt){
        return new Promise((resolve, reject) => {
            socket.emit("speak", prompt);
            setTimeout(1000);

            promptRecognition.stop(); // NOTE #1

            promptRecognition.start();

            promptRecognition.onresult = function(event){
                console.log(event)
                promptRecognition.stop();
                resolve(event.results[0][0].transcript);
            }
            promptRecognition.onnomatch = function() {
                promptRecognition.stop();
                reject(promptListen(prompt));
            }
        })

    }

The issue with the current functionality is that it doesn't work at all. I am prompted for the "Who shall I send it to?" and Jarvis listens. I get the TO console output, however, after that I get an error:

responses.js:63 Uncaught (in promise) DOMException: Failed to execute 'start' on 'SpeechRecognition': recognition has already started.
    at Promise (http://localhost:3000/scripts/responses.js:63:31)
    at new Promise (<anonymous>)
    at Respond.promptListen (http://localhost:3000/scripts/responses.js:57:16)
    at promptListen.then (http://localhost:3000/scripts/responses.js:41:28)

The error suggests that I am running SpeechRecognition already, however, I stop it at the start of promptListen() every time just in case. I do not know what is going on at all. The desired outcome would be that each call to promptListen() will allow me to prompt the user, listen to the response, store the response in a variable: for each variable needed in order to send the email (recipient, subject, and body). I am not familiar with JavaScript, promises, async, or await. I have tried every single variation possible as well as took a look at documentation to no avail. If I do not use promises, the promptListen() function is called at the same time for the recipient, subject, and body prompts. This is why I decided to try to resolve the issue with promises. Any help appreciated - Thank you!


Solution

  • My code works theoretically. The issue, or at least I think it is, is that when I call then promptRecoginition.stop() needs time to stop. I immediately call .start() again for the next prompt, once I resolve the recent promptListen(), therefore promptRecognition doesn't have time to .stop(). The solution that I have found to work is:

    setTimeout( async () => {
        await this.promptListen("What shall the subject be").then( (subject) => {
            SUBJECT = subject;
            // Add another setTimeout here
        }
    }, 1000);
    

    Using the setTimeout function, I give promptRecognition time to .stop() and continue on without any errors.