javascript 同期処理 終わっ - JavaScriptのサイクルの非同期




7 Answers

スクリプトをブロックしてブラウザをブロックすると、JavaScriptで同期と非同期を混在させることはできません。

あなたは完全なイベント駆動の方法をここで行く必要があります、運良く醜いものを隠すことができます。

編集:コードを更新しました。

function asyncLoop(iterations, func, callback) {
    var index = 0;
    var done = false;
    var loop = {
        next: function() {
            if (done) {
                return;
            }

            if (index < iterations) {
                index++;
                func(loop);

            } else {
                done = true;
                callback();
            }
        },

        iteration: function() {
            return index - 1;
        },

        break: function() {
            done = true;
            callback();
        }
    };
    loop.next();
    return loop;
}

これは私たちに非同期loopを提供します。もちろん、ループ状態などをチェックする関数を取るためにさらに変更することができます。

今すぐテストに行く:

function someFunction(a, b, callback) {
    console.log('Hey doing some stuff!');
    callback();
}

asyncLoop(10, function(loop) {
    someFunction(1, 2, function(result) {

        // log the iteration
        console.log(loop.iteration());

        // Okay, for cycle could continue
        loop.next();
    })},
    function(){console.log('cycle ended')}
);

そして出力:

Hey doing some stuff!
0
Hey doing some stuff!
1
Hey doing some stuff!
2
Hey doing some stuff!
3
Hey doing some stuff!
4
Hey doing some stuff!
5
Hey doing some stuff!
6
Hey doing some stuff!
7
Hey doing some stuff!
8
Hey doing some stuff!
9
cycle ended
待つ シングル

私は続行する前に、非同期呼び出しを待つループが必要です。 何かのようなもの:

for ( /* ... */ ) {

  someFunction(param1, praram2, function(result) {

    // Okay, for cycle could continue

  })

}

alert("For cycle ended");

どうすればこのことができますか? あなたはなにか考えはありますか?




@Ivoが示唆しているものに代わるよりクリーンな代替方法は、コレクションの非同期呼び出しを1つだけ行う必要があると仮定した場合、 非同期メソッドキューです。

(詳細については、Dustin Diazのこの記事を参照してください)

function Queue() {
  this._methods = [];
  this._response = null;
  this._flushed = false;
}

(function(Q){

  Q.add = function (fn) {
    if (this._flushed) fn(this._response);
    else this._methods.push(fn);
  }

  Q.flush = function (response) {
    if (this._flushed) return;
    this._response = response;
    while (this._methods[0]) {
      this._methods.shift()(response);
    }
    this._flushed = true;
  }

})(Queue.prototype);

Queue新しいインスタンスを作成し、必要なコールバックを追加し、非同期応答でキューをフラッシュします。

var queue = new Queue();

queue.add(function(results){
  for (var result in results) {
    // normal loop operation here
  }
});

someFunction(param1, param2, function(results) {
  queue.flush(results);
}

このパターンの追加の利点は、複数の関数を1つではなくキューに追加できることです。

イテレーター関数を含むオブジェクトがある場合は、このキューのサポートをシーンの裏に追加し、同期的に見えるコードを書くことができますが、そうではありません。

MyClass.each(function(result){ ... })

匿名関数をすぐに実行するのではなくキューに入れ、非同期呼び出しが完了したらキューをフラッシュしてください。 これは非常にシンプルでパワフルなデザインパターンです。

PS jQueryを使用している場合は、すでにjQuery.Deferredという非同期メソッドキューがあります。




jquery.Deferredの助けを使うこともできます。 この場合、asyncLoop関数は次のようになります。

asyncLoop = function(array, callback) {
  var nextElement, thisIteration;
  if (array.length > 0) nextElement = array.pop();
  thisIteration = callback(nextElement);
  $.when(thisIteration).done(function(response) {
    // here we can check value of response in order to break or whatever
    if (array.length > 0) asyncLoop(array, collection, callback);
  });
};

コールバック関数は次のようになります。

addEntry = function(newEntry) {
  var deferred, duplicateEntry;
  // on the next line we can perform some check, which may cause async response.
  duplicateEntry = someCheckHere();
  if (duplicateEntry === true) {
    deferred = $.Deferred();
    // here we launch some other function (e.g. $.ajax or popup window) 
    // which based on result must call deferred.resolve([opt args - response])
    // when deferred.resolve is called "asyncLoop" will start new iteration
    // example function:
    exampleFunction(duplicateEntry, deferred);
    return deferred;
  } else {
    return someActionIfNotDuplicate();
  }
};

遅延を解決する関数の例:

function exampleFunction(entry, deffered){
  openModal({
    title: "what should we do with duplicate"
    options: [
       {name:"Replace", action: function(){replace(entry);deffered.resolve(replace:true)}},
       {name: "Keep Existing", action: function(){deffered.resolve(replace:false)}}
    ]
  })
}



非同期ワーカー関数someFunctionを指定すると、ループを続行するかどうかを示すresult引き数を返す関数がsomeFunctionれます。

// having:
// function someFunction(param1, praram2, resultfunc))
// function done() { alert("For cycle ended"); }

(function(f){ f(f) })(function(f){
  someFunction("param1", "praram2", function(result){
    if (result)
      f(f); // loop continues
    else
      done(); // loop ends
  });
})

ループを終了するかどうかをチェックするために、 someFunctionワーカー関数は結果関数を他の非同期操作に転送することができます。 また、関数全体をコールバックとして扱うことで、式全体を非同期関数にカプセル化することもできます。




以下は、他のものよりも読める他の例です。ここでは、 done関数、現在のループインデックス、および前の非同期呼び出しの結果(もしあれば)を取り込む関数の中で非同期関数をラップします。

function (done, i, prevResult) {
   // perform async stuff
   // call "done(result)" in async callback 
   // or after promise resolves
}

done()が呼び出されると、次の非同期呼び出しがトリガされ、done関数、現在のインデックスおよび以前の結果が再び渡されます。 ループ全体が完了すると、提供されたループcallbackが呼び出されます。

実行できるスニペットは次のとおりです。

asyncLoop({
  limit: 25,
  asyncLoopFunction: function(done, i, prevResult) {
    setTimeout(function() {
      console.log("Starting Iteration: ", i);
      console.log("Previous Result: ", prevResult);
      var result = i * 100;
      done(result);
    }, 1000);
  },
  initialArgs: 'Hello',
  callback: function(result) {
    console.log('All Done. Final result: ', result);
  }
});

function asyncLoop(obj) {
  var limit = obj.limit,
    asyncLoopFunction = obj.asyncLoopFunction,
    initialArgs = obj.initialArgs || {},
    callback = obj.callback,
    i = 0;

  function done(result) {
    i++;
    if (i < limit) {
      triggerAsync(result);
    } else {
      callback(result);
    }
  }

  function triggerAsync(prevResult) {
    asyncLoopFunction(done, i, prevResult);
  }

  triggerAsync(initialArgs); // init
}




http://cuzztuts.blogspot.ro/2011/12/js-async-for-very-cool.html

編集:

githubからのリンク: https://github.com/cuzzea/lib_repo/blob/master/cuzzea/js/functions/core/async_for.js : https://github.com/cuzzea/lib_repo/blob/master/cuzzea/js/functions/core/async_for.js

function async_for_each(object,settings){
var l=object.length;
    settings.limit = settings.limit || Math.round(l/100);
    settings.start = settings.start || 0;
    settings.timeout = settings.timeout || 1;
    for(var i=settings.start;i<l;i++){
        if(i-settings.start>=settings.limit){
            setTimeout(function(){
                settings.start = i;
                async_for_each(object,settings)
            },settings.timeout);
            settings.limit_callback ? settings.limit_callback(i,l) : null;
            return false;
        }else{
            settings.cbk ? settings.cbk(i,object[i]) : null;
        }
    }
    settings.end_cbk?settings.end_cbk():null;
    return true;
}

この関数を使用すると、forループでsettings.limitを使用してパーセントブレークを作成できます。 limitプロパティは単なる整数ですが、array.length * 0.1として設定すると、settings.limit_callbackが10%ごとに呼び出されます。

/*
 * params:
 *  object:         the array to parse
 *  settings_object:
 *      cbk:            function to call whenwhen object is found in array
 *                          params: i,object[i]
 *      limit_calback:  function to call when limit is reached
 *                          params: i, object_length
 *      end_cbk:        function to call when loop is finished
 *                          params: none
 *      limit:          number of iteration before breacking the for loop
 *                          default: object.length/100
 *      timeout:        time until start of the for loop(ms)
 *                          default: 1
 *      start:          the index from where to start the for loop
 *                          default: 0
 */

例:

var a = [];
a.length = 1000;
async_for_each(a,{
    limit_callback:function(i,l){console.log("loading %s/%s - %s%",i,l,Math.round(i*100/l))}
});



いくつかの非同期関数をX回呼び出す必要がありました。前の処理が完了した後に各繰り返しが行われているに違いないので、私はこのように使用できるlitteライブラリを書きました:

// https://codepen.io/anon/pen/MOvxaX?editors=0012
var loop = AsyncLoop(function(iteration, value){
  console.log("Loop called with iteration and value set to: ", iteration, value);

  var random = Math.random()*500;

  if(random < 200)
    return false;

  return new Promise(function(resolve){
    setTimeout(resolve.bind(null, random), random);
  });
})
.finished(function(){
  console.log("Loop has ended");
});

ユーザー定義のループ関数が呼び出されるたびに、それには2つの引数、繰り返しインデックスと前回の呼び出し戻り値があります。

これは出力の例です:

"Loop called with iteration and value set to: " 0 null
"Loop called with iteration and value set to: " 1 496.4137048207333
"Loop called with iteration and value set to: " 2 259.6020382449663
"Loop called with iteration and value set to: " 3 485.5400568702862
"Loop has ended"



Related


Tags

javascript