I am creating a nodeJS application that takes a message from the Character.AI API and then passes that message into the say module’s speak command. Before I just put the message into the speak command, but the message has parts where the character refers to themselves in 3rd person (represented as a part of the message with an * at the beginning and end of it*).
My goal with this function is to have the speak command iterate through a list that was created by slicing the original string based on if there was a new line in it. Then to check each list element to see if that element has an (*) at the beginning and end of it.
If it doesn’t have that pattern I use a version of the speak command that has one voice representing one character speak, and if it does follow the pattern I use a version of the speak command that has another system voice to represent a narrator.
The speak command was not built with async in mind (the module is pretty old), so I am trying to wrap it into a promise, push those promises into a list, and then await
each iterate of that list so that I can make each message play out completely before I move on to the next message.
I have a snippet of some of the code that I am working on. The promises DO resolve and so that should mean that the speak command actually speaks out the message. But only silence is let out. However, if I move the speak command out of the loop the speak command fires as expected.
If anyone has experience using the say module, could you please explain why this behavior is happening and any possible solutions?
if(responseData.length == 0){
say.speak("Sorry. I do not think I heard that right.");
}
else{
var sayPromise = [];
for(var i = 0; i < responseData.length;i++){
const resolve = () => {
return new Promise((resolve) => {
say.speak(responseData[i],"Alex",1,(err)=>{
resolve();
});
})};
sayPromise.push(resolve);
}
for(const async of sayPromise){
const result = await async();
} }```
The issue is, because you use for(var i
, by the time you call the function resolve
(the function you created, not the Promise resolve callback), i
is responseData.length
for every item in the array
Quick fix, use for (let i =...
since let
creates block scoped rather than function scoped variables, the value of i
will be correct when you call your function
like so
if (responseData.length == 0) {
say.speak("Sorry. I do not think I heard that right.");
} else {
const sayPromise = [];
for (let i = 0; i < responseData.length; i++) {
const resolve = () => {
return new Promise((resolve) => {
say.speak(responseData[i], "Alex", 1, (err) => {
resolve();
});
})
};
sayPromise.push(resolve);
}
for (const async of sayPromise) {
const result = await async();
}
}
Or, better yet, your whole code can be written like
if (responseData.length == 0) {
say.speak("Sorry. I do not think I heard that right.");
} else {
for (let i = 0; i < responseData.length; i++) {
await new Promise((resolve) => say.speak(responseData[i], "Alex", 1, resolve));
}
}
And, in my opinion, even betterer - using for...of
if (responseData.length == 0) {
say.speak("Sorry. I do not think I heard that right.");
} else {
for (let response of responseData) {
await new Promise((resolve) => say.speak(response, "Alex", 1, resolve));
}
}