使用异步函数实现for循环延迟的最佳实践是什么?

时间:2015-05-23 09:44:34

标签: javascript arrays asynchronous

这是我目前使用的代码,

function loopArrayWithAsync(array, doSthWithElement, finalCallback) {
    var count = 0;
    var _data = [];
    var _errs = [];
    for (var i = 0; i < array.length; i++) {
        doSthWithElement(array[i], function (err, data) {
            count++;
            if (err) {
                _errs.push(err);
            }
            if (data) {
                _data.push(data);
            }
            if (count === data.length) {
                finalCallback(_errs, _data);
            }
        }
    }
}

然后,我将以这种方式使用该功能:

loopArrayWithAsync(array, function (element, finish) {
    // element - element in the array
    asyncFunc(element, function (err, result) {
        if (err) {
            finish(err);
        } else {
            finish(null, result);
        }
    });
}, function (errs, finalData) {
    // when the for loop is finished,
    // i.e. (count === data.length)
    // this callback will be executed
    // errs - array of err from the above callback function
    // finalData - array of result from the above callback function
    outerCallback(errs, finalData);
});

通过这个实现,我可以循环使用异步函数的数组,并在处理完数组中的所有元素时执行回调函数。

但现在我想为loopArrayWithAsync()

添加延迟/间隔功能 像loopArrayWithAsync(array, {interval : 1000}, function (element, finish) {...之类的东西,在处理完第一个元素之后,应该等待1000毫秒,然后开始处理第二个元素,反之亦然...

我发现另一个question正在谈论为for loop添加延迟

但在处理异步函数时似乎更复杂。

任何答案都将不胜感激

============================== update ================ ==============

这是重构后的功能,

function loopArrayWithAsync(array, options, doSthWithElement, finalCallback) {
    if (isFunction(options)) {
        finalCallback = doSthWithElement;
        doSthWithElement = options;
        options = {};
    }
    options.interval = options.interval || 0;
    options.oneByOne = options.oneByOne || false;
    var _data = [];
    var _errs = [];
    var count = 0;
    var length = array.length;
    var i = 0;
    (function handleIteration() {
        if (i < length) {
            var element = array[i];
            doSthWithElement(element, function (err, data) {
                if (err) {
                    _errs.push(err);
                }
                if (data) {
                    _data.push(data);
                }
                count++;
                if (count === length) {
                    finalCallback(_errs, _data);
                } else if (options.oneByOne) {
                    if (options.interval) {
                        setTimeout(handleIteration, options.interval);
                    } else {
                        process.nextTick(handleIteration);
                    }
                }
            });
            i++;
            if (!options.oneByOne) {
                if (options.interval) {
                    setTimeout(handleIteration, options.interval);
                } else {
                    process.nextTick(handleIteration);
                }
            }
        }
    }());
};

这样我现在就可以这样使用这个功能了:

loopArrayWithAsync(array, {interval : 1000}, function (element, finish) {
    asyncFunc(element, function (err, result) {
        if (err) {
            finish(err);
        } else {
            anotherAsyncFunc(result, function (err, doc) {
                if (err) {
                    finish(err);
                } else {
                    finish(null, doc);
                }
            });
        }
    });
}, function (errs, finalData) {
    outerCallback(errs, finalData);
});

loopArrayWithAsync(array, {oneByOne : true}, function (element, finish) {... 逐个循环遍历元素

loopArrayWithAsync(array, {interval : 5000, oneByOne : true}, function (element, finish) {... 逐个循环遍历元素并延迟5秒

可用选项:

interval是每次迭代之间的毫秒数,默认值为:0

如果oneByOne为真,则该方法只会在为当前元素调用finish之前进入下一个元素,默认值为:false

代码现在适合我的情况,但我仍然会尝试使用建议的库来让生活更轻松,谢谢

如果您发现代码可以进一步改进,请发表评论,期待任何建议!

2 个答案:

答案 0 :(得分:0)

您可以使用本地函数进行异步循环。对于下一次迭代,函数调用自身的延迟:

function loopArrayWithAsync(array, doSthWithElement, finalCallback, delay) {
  var _data = [], _errs = [], i = 0;
  loop();

  function loop() {
    doSthWithElement(array[i], function (err, data) {
      if (err) {
        _errs.push(err);
      }
      if (data) {
        _data.push(data);
      }
      i++;
      if (i === array.length) {
        finalCallback(_errs, _data);
      } else {
        window.setTimeout(loop, delay);
      }
    }
  }

}

要以特定间隔开始通话而不是在通话之间有延迟,只需在不同时间使用setInterval

function loopArrayWithAsync(array, doSthWithElement, finalCallback, delay) {
  var _data = [], _errs = [], count = 0;
  for (var i = 0; i < array.length; i++) {
    window.setTimeout(function() {
      doSthWithElement(array[i], function (err, data) {
        if (err) {
          _errs.push(err);
        }
        if (data) {
          _data.push(data);
        }
        count++;
        if (count === array.length) {
          finalCallback(_errs, _data);
        }
      });
    }, i * delay);
  }
}

答案 1 :(得分:0)

根据@thefourtheye的建议,您可以使用Promises的概念,Bluebird是一个快速而好的库。 Promise.settle可让您解决并拒绝您的承诺,然后检查结果。

function loopArray(array) {
    var arrayOfPromises = [];
    for (var i = 0; i < array.length; i++) {
        arrayOfPromises.push(doSomethingAsync(array[i]));
    }

    Promise.settle(arrayOfPromises).then(function (results) {
        console.log("All async calls done! You can inspect the result!");
        console.log(results);
    });
}


function doSomethingAsync(item) {
    return new Promise(function(resolve, reject){
        //Do you async work here!
        console.log("Entering async function call " + item);
        if(item === "three"){
            reject("bad value!");
        }
        resolve(item + " promise done!");
    });
}


loopArray(["one","two","three"]);

我制作了以下示例的JSFiddle。使用异步函数,promises可以帮助你很多,所以我真的建议你调查它。

相关问题