6

重複の可能性:
javascript: 1 つのコールバックで一連の非同期メソッドを実行する

私はこの問題に何日も苦労してきましたが、それを処理するエレガントな方法がわかりません。これが問題です。

forEach ループを実行していますが、すべてがいつ完了したかを知る必要があります。forEach ループはブロックしているため、 forEach ループの後に a をドロップするだけで十分簡単console.logです。これは、 forEach ループが完了したときに実行されます。ただし、forEach ループ内に同期していない関数がある場合、このシステム全体がすぐに機能しなくなり、非常に奇妙なハッキー カウンターと if ステートメント以外に、ループの内容がいつ完了したかを知る方法がありません。私の質問は、JavaScript でこの状況を処理するエレガントな方法があるかどうかです。

以下にいくつかの例を示します。

この場合、ループ内のすべてが同期されるため、forEach ループは完全にうまく機能します。

[1,2,3].forEach(function(num){
  console.log("we're on " + num);
});
console.log('done counting!')

//=> we're on 1
//=> we're on 2
//=> we're on 3
//=> done counting!

ただし、基本的な例として、ここで問題が発生し始めます。

[1,2,3].forEach(function(num){
  setTimeout(function(){ console.log(num) }, 200);
});
console.log('done counting!');

//=> done counting!
//=> we're on 1
//=> we're on 2
//=> we're on 3

これが起こっていることは理にかなっていますが、カウントが完了したときにコールバックが必要であるという不幸な問題を抱えており、次のようなひどいもの以外にコールバックをスムーズに行う方法はありません。

var counter = 0;
var numbers = [1,2,3]

var log_number = function(num){
  console.log("we're on " + num);
  counter++;
  if (counter == numbers.length) {
    console.log("done counting!")
  }
}

numbers.forEach(function(num){
  setTimeout(log_number(num), 200);
});

//=> we're on 1
//=> we're on 2
//=> we're on 3
//=> done counting!

よかった、それは非常に多くのオーバーヘッドです。これを実現するよりスムーズな方法はありますか?promise、deferred、およびシーケンスをチェックアウトしましたが、これをより簡潔にする方法でそれらのいずれかを実際に実装することはできませんでした:(

明確にするために、特に私の問題は setTimeout ではありません。一般に、同期関数内で非同期関数が実行されています。これは最も単純な例にすぎません。

4

6 に答える 6

7
Array.prototype.forEachDone = function(fn, scope, lastfn) {
    for(var i = 0, c = 0, len = this.length; i < len; i++) {
        fn.call(scope, this[i], i, this, function() {
            ++c === len && lastfn();
        });
    }
};

[1,2,3].forEachDone(function(num, i, arr, done){
  setTimeout(function(){ console.log(num); done() }, 200);
}, this, function() {
    console.log('done counting!');
});

これはあなたが探していることをします。forEachDoneは、forEachとまったく同じパラメーターを取りますが、最後に追加のパラメーターがあり、配列の各要素に適用された関数が実行されたときに呼び出されるコールバックです。配列内の各要素に適用される関数も、forEachの場合とまったく同じパラメーターを取りますが、関数の終了時に呼び出す必要のある関数である追加のパラメーターも取ります。

PS:個人的には、ネイティブオブジェクトのプロトタイプには触れませんが、きれいなものを探しているなら、これがそれです。

于 2012-11-04T02:54:45.917 に答える
4
var list = ["your", "mom"];
var count = 0;

for(var i = 0; i < list.length; ++i) {
    getSomething(list[i], function() {
     if(++count == list.length)
      imDone();
     });
}

別の方法はこのようなものです

var biscuits = ["cheddar", "butter milk"];

(function eatBiscuits(location) {
  eatIt(biscuits[++location], whenAllEaten);
  typeof biscuits[location] != "undefined" && eatBiscuits(location);
}(-1))

function eatIt(biscuit, cb) {
 if (typeof biscuit == "undefined") {
  cb();
 } else {
  stuffMyFace();
 }
}
于 2012-11-04T00:47:03.017 に答える
1

私は次のような小さなユーティリティ関数を使用する傾向があります。

​function asyncCallback( iterations, onComplete ){
  var total = iterations.length || iterations, count = 0;
  return function( func ){
    func();
    if ( ++count == total ) onComplete();
  };
}

var arr = [1,2,3];

var async = asyncCallback(arr, function(){
  console.log('donezo!');
});

arr.forEach(function(num){
  setTimeout(function(){
    async(function(){
      console.log(num);
    });
  }, 200);
});

</p>

于 2012-11-04T02:21:24.753 に答える
1

lib を使用する場合は、asyncを選択することをお勧めします (ブラウザでも node.js でも動作します)。

async.parallel([
    function(){ ... },
    function(){ ... }
], callback);

async.series([
    function(){ ... },
    function(){ ... }
]);
于 2012-11-04T01:36:27.500 に答える
0

実際には、それを行うための「賢い」方法はありません。setTimeout、、 setIntervalの ようなものを使用する必要がありますWorker

于 2012-11-04T00:31:31.277 に答える
-1
[1,2,3,4,5,6,7,8].forEach(function(n){
 setTimeout(function(){
  console.log(arguments[0]);
 },1000*n,n);
});

これは私にとってはうまくいきます

于 2012-11-04T00:30:20.293 に答える