0

I am currently using amazon polly to generate sound files from data however when I run my script I keep getting the error message:

ThrottlingException: Rate exceeded.

What I have currently

Here is the function that I use

function createAudioFiles(data, outputDir) {
  console.log('in createAudioFiles with data: ' , data);

  return new Promise((resolve, reject) => {
    let successessfullyCompletedAmount = 0;

    for ({ audioText, filename } of data) {

        createAudio(audioText, filename, outputDir)
        .then(({ status, message }) => {
          if (status == "success") {
            successessfullyCompletedAmount++;
            console.log(message);
          }

          // if all audio files have been created
          if (successessfullyCompletedAmount == data.length) {
            resolve({
              status: 'success',
              message: "successfully created audioFiles"
            })
          }

        })

    }
  });
}

The createAudio function in that function is:

function createAudio(text, filename, outputDir) {

  let params = {
    'Text': text,
    'OutputFormat': 'mp3',
    'VoiceId': 'Amy'
  };

  return new Promise((resolve, reject) => {

        Polly.synthesizeSpeech(params, (err, data) => {

            if (err) {
                console.log("errors found");
                console.log(err);
            } else if (data) {
                if (data.AudioStream instanceof Buffer) {
                    fs.writeFile(`${outputDir}/${filename}`, data.AudioStream, function (err) {
                        if (err) {
                            return reject({
                                status: "error",
                                message: err
                            })
                        }
                        resolve({
                            status: "success",
                            message: filename + " saved"
                        });
                    })
                }
            }

        });

  })



}.

What I tried.

I tried adding a setTimeout() wrapper on the for loop like so:

setTimeout(function() {
        for ({ audioText, filename } of data) {

            createAudio(audioText, filename, outputDir)
            .then(({ status, message }) => {
            if (status == "success") {
                successessfullyCompletedAmount++;
                console.log(message);
            }

            // if all audio files have been created
            if (successessfullyCompletedAmount == data.length) {
                resolve({
                status: 'success',
                message: "successfully created audioFiles"
                })
            }

            })
        }
    }, 2000);

but that does not seem to change anything. What I think is happening is all the data seems to be gathered and sent at once.

What I want to happen.

I would like to add a small delay between each 'get' from amazon to slow down the rate.

modusTollens
  • 397
  • 7
  • 23
  • See also [Rate limiting a queue of API calls and returning the results](https://stackoverflow.com/q/63436910/1048572) – Bergi Aug 16 '20 at 16:49

1 Answers1

0

to add some delay between each createAudio call, you can do something like this :

return new Promise((resolve, reject) => {
    let successessfullyCompletedAmount = 0;
    let timeOutputDelay = 0;
    for ({ audioText, filename } of data) {
        setTimeout(function() {
            createAudio(audioText, filename, outputDir)
                .then(({ status, message }) => {
                    // [...]
                })
        } 1000 * timeOutputDelay);
        timeOutputDelay++;
    }
});

each setTimeout() will have delay base on how many items are already looped.

This solution is not perfect because you don't wait for previous generation done before starting next one.

So another approch is to chain all Promise.then by using :

  • array of audio generation.
  • Array.reduce helper.

With this approch you will chain each item together, and your code will wait end of current before starting next one.

const createDummyAsyncPromise = (audio : {text: string, delay: number}) => {
  return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(`Mp3 generated for text : ${audio.text}`);
      }, audio.delay);
  });
}

const audios = [
  {text: 'text 1', delay: 2000},
  {text: 'text 2', delay: 200},
  {text: 'text 3', delay: 10},
  {text: 'text 4', delay: 1000}
];
audios.reduce((promiseChain, audio) => {
    return promiseChain.then(audioResults =>
        createDummyAsyncPromise(audio).then(audioResult =>
            [ ...audioResults, audioResult ]
        )
    );
}, Promise.resolve([])).then(audios => {
    // Do something with all results
    console.log(audios);
});

live sample

Yanis-git
  • 7,737
  • 3
  • 24
  • 41