首页 > 解决方案 > for循环内的异步回调

问题描述

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);
})

在上面的代码中,gtts.savetos3是一个异步函数。完成数组中每个元素的执行需要花费大量时间。由于异步功能,我无法在op数组中打印完整的 url 数组,因为它会打印一个空数组.

gtts.savetos3函数使用正确的 url 调用给定的回调,以便我可以使用打印输出,console.log但是在循环时我搞砸了。

我的问题是

  1. 如何使回调函数仅在所有数组元素执行后才被gtts.savetos3函数处理。
  2. 我们可以在没有Promise.all或没有Promise的情况下仅使用回调来解决上述问题吗?

提前致谢 ...!

标签: javascriptasynchronouscallback

解决方案


您可以保留一个计数器并在方法的回调中增加它,done仅当计数器达到数组的长度时才调用您的回调。

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);
})

这只是在没有Promises或任何第三方模块的情况下解决问题的一种方法,而不是优雅或正确的方法。

如果您想坚持回调并且可以使用第三方模块,请查看 Async waterfall method

如果您正在使用aws-sdk's s3 put object,那么 sdk 也已经提供了一种承诺方法,您可以简单地附加您的方法 .promise来获得相同的方法。

要解决 Promise 的问题,只需将包装器更改为异步函数即可。

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);
})

推荐阅读