-1
var a = ['url1', 'url2', 'url3'];
var op = [];

cb = (callback) => {
  for (var i = 0; i < a.length; i++) {
    gtts.savetos3(`${a[i]}.mp3`, a[i], 'ta', function(url) {
      console.log(url['Location']);
      op.push(url['Location']);
    });
  }
  callback()
}

cb(() => {
   console.log(op);
})

In the above code the gtts.savetos3 is an asynchronous function.It takes significance amount of time to complete the execution for every element in array.Due to asynchronous feature I cannot print the complete array of url in op array as it prints an empty array.

gtts.savetos3 function calls the given callback with correct url so that i can print the output using console.log but when comes to looping i got messed up.

My question is

  1. How to make the callback function called only after the execution of the all array elements get processed by the gtts.savetos3 function.
  2. can we achieve the solution for above problem without Promise.all or without Promise with the help of using only callbacks.

Thanks in Advance ...!

adiga
  • 34,372
  • 9
  • 61
  • 83
chellathurai
  • 115
  • 1
  • 1
  • 8
  • you can use `await` – Pranav C Balan Jan 14 '20 at 07:29
  • 2
    Why don't you want to use `Promise.all`? It *is* by far the most elegant solution https://stackoverflow.com/questions/31426740/how-to-return-many-promises-in-a-loop-and-wait-for-them-all-to-do-other-stuff There are workarounds, sure, but I wouldn't want to recommend them – CertainPerformance Jan 14 '20 at 07:32
  • what is `gtts.savetos3` is it `aws-sdk`'s method? – AZ_ Jan 14 '20 at 07:35
  • It is customized method which uses aws-sdk. – chellathurai Jan 14 '20 at 07:58
  • @CertainPerformance could you provide the simple example which has the similar pattern using promises.All ? – chellathurai Jan 14 '20 at 08:04
  • See https://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises and https://stackoverflow.com/questions/31426740/how-to-return-many-promises-in-a-loop-and-wait-for-them-all-to-do-other-stuff – CertainPerformance Jan 14 '20 at 08:05

2 Answers2

2

You can keep a counter and increase it inside the methods's callback, call your done callback only when the counter reaches the length of the array.

cb = (done) => {
    let counter = 0;
    for (let i = 0; i < a.length; i++) {
        gtts.savetos3(`${a[i]}.mp3`, a[i], 'ta', function (url) {
            console.log(url['Location']);
            op.push(url['Location']);
            ++counter;
            if (counter == a.length) {
              done();
            }
        });
    }
}

cb(() => {
    console.log(op);
})

This is just a way to solve the problem without Promises or any third party module but not the elegant or correct way.

If you want to stick to the callback and are ok to use third-party module have a look on Async waterfall method.

If you are using aws-sdk's s3 put object, then sdk already provides a promisified methods as well, you can simply append your method with .promise to get the same.

To solve the problem with promises just change your wrapper into an async function.

async savetos3(...parametres) {
    //Some implementation
    let res = await S3.putObject(....params).promise();
    //Some implementation
}

cb = Promise.all(a.map(name => savetos3(`${name}.mp3`, name , 'ta')));

cb.then(() => {
    console.log(op);
})
AZ_
  • 3,094
  • 1
  • 9
  • 19
0

Here is my solution.

const a = ['url1', 'url2', 'url3'];
const op = [];

const saveToS3 = name => new Promise((resolve, reject) => {
    gtts.savetos3(`${name}.mp3`, name, 'ta', function (url) {
        console.log(url['Location']);
        resolve(url)
    });
})

Promise.all(a.map(item => saveToS3(item))).then(() => {
    console.log(op)
})
TopW3
  • 1,477
  • 1
  • 8
  • 14