私は次のコードを持っています:
_for(var i = 0; i < list.length; i++){
mc_cli.get(list[i], function(err, response) {
do_something(i);
});
}
_
_mc_cli
_は、memcachedデータベースへの接続です。ご想像のとおり、コールバック関数は非同期であるため、forループが既に終了したときに実行される場合があります。また、この方法でdo_something(i)
を呼び出すと、常にforループの最後の値が使用されます。
このようにクロージャーを試してみました
_do_something((function(x){return x})(i))
_
しかし明らかに、これはforループのインデックスの最後の値を常に使用しているようです。
また、次のようにforループの前に関数を宣言しようとしました。
_var create_closure = function(i) {
return function() {
return i;
}
}
_
そして、呼び出す
_do_something(create_closure(i)())
_
ただし、これも成功せず、戻り値は常にforループの最後の値になります。
誰かが私がクロージャーで間違っていることを教えてもらえますか?私はそれらを理解したと思ったが、なぜこれが機能しないのか理解できない。
配列を実行しているので、リスト項目とコールバックのインデックスを提供するforEach
を単純に使用できます。反復には独自のスコープがあります。
list.forEach(function(listItem, index){
mc_cli.get(listItem, function(err, response) {
do_something(index);
});
});
これは非同期関数内部ループのパラダイムであり、通常は即時呼び出し匿名関数を使用して処理します。これにより、インデックス変数の正しい値で非同期関数が呼び出されます。
いいですねしたがって、すべての非同期関数が起動され、ループが終了します。現在、これらの関数が非同期の性質のため、いつ完了するのか、いつ完了するのかはわかりません。これらのすべての関数が完了するまで待機してから実行する必要があるコードがある場合、終了した関数の数を単純に数えることをお勧めします。
var total = parsed_result.list.length;
var count = 0;
for(var i = 0; i < total; i++){
(function(foo){
mc_cli.get(parsed_result.list[foo], function(err, response) {
do_something(foo);
count++;
if (count > total - 1) done();
});
}(i));
}
// You can guarantee that this function will not be called until ALL of the
// asynchronous functions have completed.
function done() {
console.log('All data has been loaded :).');
}
私はこれが古いスレッドであることを知っていますが、とにかく私の答えを追加します。 ES2015 let
には、反復ごとにループ変数を再バインドする機能があるため、非同期コールバックでループ変数の値が維持されるため、次のいずれかを試すことができます。
for(let i = 0; i < list.length; i++){
mc_cli.get(list[i], function(err, response) {
do_something(i);
});
}
ただし、とにかく、forEach
はES2015の機能であり、すべてのブラウザーと実装をサポートしているわけではないため、let
を使用するか、即時呼び出し関数を使用してクロージャーを作成することをお勧めします。 From here under Bindings ->let->for/for-in loop iteration scope
Edge 13まで、およびFirefox 49までサポートされていないことがわかります(これらはチェックインしていませんブラウザ)。 Node 4ではサポートされていませんが、私は個人的にテストし、サポートされているようです。
かなり近かったですが、クロージャーをコールバック内に置くのではなく、get
に渡す必要があります。
function createCallback(i) {
return function(){
do_something(i);
}
}
for(var i = 0; i < list.length; i++){
mc_cli.get(list[i], createCallback(i));
}
_async/await
_構文とPromise
を使用して、これを試してください
_(async function() {
for(var i = 0; i < list.length; i++){
await new Promise(next => {
mc_cli.get(list[i], function(err, response) {
do_something(i); next()
})
})
}
})()
_
これは、next()
関数がトリガーされるまで、各サイクルでループを停止します
ES2017:非同期コードを関数(XHRPostなど)内にラップして、promise(promise内の非同期コード)を返すことができます。
次に、forループ内で、魔法のAwaitキーワードを使用して関数(XHRPost)を呼び出します。 :)
let http = new XMLHttpRequest();
let url = 'http://sumersin/forum.social.json';
function XHRpost(i) {
return new Promise(function(resolve) {
let params = 'id=nobot&%3Aoperation=social%3AcreateForumPost&subject=Demo' + i + '&message=Here%20is%20the%20Demo&_charset_=UTF-8';
http.open('POST', url, true);
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.onreadystatechange = function() {
console.log("Done " + i + "<<<<>>>>>" + http.readyState);
if(http.readyState == 4){
console.log('SUCCESS :',i);
resolve();
}
}
http.send(params);
});
}
for (let i = 1; i < 5; i++) {
await XHRpost(i);
}
ループ内で非同期関数を実行したいが、コールバックが実行された後もインデックスまたは他の変数を保持したい場合は、コードをIIFE(すぐに呼び出される関数式)でラップできます。
var arr = ['Hello', 'World', 'Javascript', 'Async', ':)'];
for( var i = 0; i < arr.length; i++) {
(function(index){
setTimeout(function(){
console.log(arr[index]);
}, 500);