Không đồng bộ cho chu kỳ trong JavaScript


87

Tôi cần một vòng lặp chờ cuộc gọi không đồng bộ trước khi tiếp tục. Cái gì đó như:

for ( /* ... */ ) {

  someFunction(param1, praram2, function(result) {

    // Okay, for cycle could continue

  })

}

alert("For cycle ended");

Làm thế nào tôi có thể làm điều này? Bạn có bất cứ ý tưởng?


128
Wow ( /* ... */ )trông giống như một con quái vật và bây giờ tôi đang sợ hãi :(
Pointy vào

Câu trả lời:


182

Bạn không thể kết hợp đồng bộ và không đồng bộ trong JavaScript nếu bạn chặn tập lệnh, bạn chặn Trình duyệt.

Bạn cần phải đi theo hướng sự kiện đầy đủ ở đây, may mắn là chúng ta có thể giấu những thứ xấu xí đi.

CHỈNH SỬA: Đã cập nhật mã.

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

Điều này sẽ cung cấp cho chúng tôi một không đồng bộ loop, tất nhiên bạn có thể sửa đổi nó hơn nữa để lấy ví dụ như một hàm để kiểm tra điều kiện vòng lặp, v.v.

Bây giờ đến bài kiểm tra:

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

Và đầu ra:

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

28
có thể tôi đang thiếu một cái gì đó, nhưng tôi không hiểu làm thế nào điều này thực sự không đồng bộ. Bạn không cần setTimeout hay gì đó? Tôi đã thử mã của bạn, lấy ra console.log và đếm rất nhiều và nó chỉ đóng băng trình duyệt.
tên lửa, bữa sáng

Tôi bối rối không biết loop.break()phải làm gì? Chỉ là một cách để buộc ra nếu bạn muốn?
whitfin

2
như tên lửa đã nói ở trên, câu trả lời này không phải là không đồng bộ và do đó hoàn toàn sai!
kofifus

giống như loop.next, loop.break nên được bỏ qua khi thực hiện là đúng: `break: function () {if (! done) {done = true; gọi lại(); }} `
db-inf

4
Xin lỗi, đã phải từ chối vì điều này không thực sự là không đồng bộ.
Rikaelus

44

Tôi đã đơn giản hóa điều này:

CHỨC NĂNG:

var asyncLoop = function(o){
    var i=-1;

    var loop = function(){
        i++;
        if(i==o.length){o.callback(); return;}
        o.functionToLoop(loop, i);
    } 
    loop();//init
}

SỬ DỤNG:

asyncLoop({
    length : 5,
    functionToLoop : function(loop, i){
        setTimeout(function(){
            document.write('Iteration ' + i + ' <br>');
            loop();
        },1000);
    },
    callback : function(){
        document.write('All done!');
    }    
});

VÍ DỤ: http://jsfiddle.net/NXTv7/8/


+1 Tôi đã làm điều gì đó tương tự như điều này, nhưng tôi đã đặt phần setTimeout trong hàm thư viện.
tên lửa bữa sáng

Về cơ bản không phải là một đệ quy?
Pavel

7

Một giải pháp thay thế rõ ràng hơn cho những gì @Ivo đã đề xuất sẽ là Hàng đợi phương pháp không đồng bộ , giả sử rằng bạn chỉ cần thực hiện một lệnh gọi không đồng bộ cho bộ sưu tập.

(Xem bài đăng này của Dustin Diaz để được giải thích chi tiết hơn)

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

Bạn chỉ cần tạo một phiên bản mới Queue, thêm các lệnh gọi lại mà bạn cần, sau đó xóa hàng đợi bằng phản hồi không đồng bộ.

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

Một lợi ích bổ sung của mẫu này là bạn có thể thêm nhiều hàm vào hàng đợi thay vì chỉ một.

Nếu bạn có một đối tượng chứa các hàm của trình lặp, bạn có thể thêm hỗ trợ cho hàng đợi này ở phía sau và viết mã trông đồng bộ, nhưng không phải:

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

chỉ cần viết eachđể đưa hàm ẩn danh vào hàng đợi thay vì thực thi nó ngay lập tức, rồi xóa hàng đợi khi lệnh gọi không đồng bộ của bạn hoàn tất. Đây là một mẫu thiết kế rất đơn giản và mạnh mẽ.

PS Nếu bạn đang sử dụng jQuery, bạn đã có sẵn một hàng đợi phương thức không đồng bộ có tên jQuery.Deferred .


1
Nếu hiểu câu hỏi một cách chính xác thì điều này sẽ không mang lại hành vi mong muốn, có vẻ như cô ấy muốn thực hiện một số lệnh gọi lại someFunctionđể trì hoãn phần còn lại của vòng lặp, mẫu của bạn thiết lập một danh sách các hàm sẽ được thực thi theo thứ tự và tất cả sẽ nhận được kết quả của một lệnh gọi hàm khác. Đó là một mô hình tốt nhưng tôi không nghĩ rằng nó phù hợp với câu hỏi được đề cập.
Ivo Wetzel

@Ivo Nếu không có thêm thông tin, chúng tôi sẽ không biết chắc chắn, nhưng nói một cách tổng quát, tôi nghĩ rằng việc làm cho mã đồng bộ phải chờ một hoạt động không đồng bộ trước khi tiếp tục là một thiết kế tồi; trong mọi trường hợp tôi đã thử, nó dẫn đến độ trễ giao diện người dùng đáng chú ý do JS là một luồng. Nếu thao tác diễn ra quá lâu, bạn có nguy cơ trình duyệt buộc phải dừng tập lệnh của mình.
Adam Lassek

@Ivo cũng vậy, tôi rất cảnh giác với mã dựa vào setTimeout. Bạn có nguy cơ xảy ra hành vi không mong muốn nếu mã được thực thi nhanh hơn bạn dự đoán.
Adam Lassek

@Adam Theo cách nào tôi sẽ mạo hiểm hành vi không mong muốn với setTimeoutđiều gì xảy ra nếu cuộc gọi lại chỉ mất một nửa thời gian, mã thực thi nhanh hơn một lần nữa ... vậy điểm mấu chốt là gì? "Mã" trong "vòng lặp" vẫn theo thứ tự, nếu bạn thực hiện một số nội dung bên ngoài nó trước khi gọi lại hoàn chỉnh, bạn đã gặp sự cố, nhưng sau đó lại là một chuỗi đơn lẻ, tôi gặp khó khăn khi nghĩ ra tình huống setTimeoutsẽ phá vỡ một cái gì đó, mà không cần thiết kế sai thêm.
Ivo Wetzel

Ngoài ra, anh ấy đã yêu cầu một mô-đun Node.js như thế này trong một câu hỏi khác, tôi đã nói ở đó rằng nói chung là một ý tưởng tồi khi có một giải pháp chung cho các vòng lặp "async-sync" như vậy. Tôi muốn đi với một cái gì đó phù hợp với yêu cầu chính xác của bất cứ điều gì tôi đang cố gắng đạt được.
Ivo Wetzel

3

Cũng nhìn vào thư viện caolan / async lộng lẫy này . forVòng lặp của bạn có thể dễ dàng được thực hiện bằng cách sử dụng mapSeries hoặc series .

Tôi có thể đăng một số mã mẫu nếu ví dụ của bạn có thêm chi tiết trong đó.


2

Chúng ta cũng có thể sử dụng sự trợ giúp của jquery.Deferred. trong trường hợp này, hàm asyncLoop sẽ giống như sau:

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

hàm gọi lại sẽ giống như sau:

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

hàm ví dụ giải quyết trì hoãn:

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

2

Tôi đã sử dụng "setTimeout (Func, 0);" lừa trong khoảng năm. Đây là một số nghiên cứu gần đây tôi đã viết để giải thích cách tăng tốc độ một chút. Nếu bạn chỉ muốn có câu trả lời, hãy chuyển sang Bước 4. Bước 1 2 và 3 giải thích lý do và cơ học;

// In Depth Analysis of the setTimeout(Func,0) trick.

//////// setTimeout(Func,0) Step 1 ////////////
// setTimeout and setInterval impose a minimum 
// time limit of about 2 to 10 milliseconds.

  console.log("start");
  var workCounter=0;
  var WorkHard = function()
  {
    if(workCounter>=2000) {console.log("done"); return;}
    workCounter++;
    setTimeout(WorkHard,0);
  };

// this take about 9 seconds
// that works out to be about 4.5ms per iteration
// Now there is a subtle rule here that you can tweak
// This minimum is counted from the time the setTimeout was executed.
// THEREFORE:

  console.log("start");
  var workCounter=0;
  var WorkHard = function()
  {
    if(workCounter>=2000) {console.log("done"); return;}
    setTimeout(WorkHard,0);
    workCounter++;
  };

// This code is slightly faster because we register the setTimeout
// a line of code earlier. Actually, the speed difference is immesurable 
// in this case, but the concept is true. Step 2 shows a measurable example.
///////////////////////////////////////////////


//////// setTimeout(Func,0) Step 2 ////////////
// Here is a measurable example of the concept covered in Step 1.

  var StartWork = function()
  {
    console.log("start");
    var startTime = new Date();
    var workCounter=0;
    var sum=0;
    var WorkHard = function()
    {
      if(workCounter>=2000) 
      {
        var ms = (new Date()).getTime() - startTime.getTime();
        console.log("done: sum=" + sum + " time=" + ms + "ms"); 
        return;
      }
      for(var i=0; i<1500000; i++) {sum++;}
      workCounter++;
      setTimeout(WorkHard,0);
    };
    WorkHard();
  };

// This adds some difficulty to the work instead of just incrementing a number
// This prints "done: sum=3000000000 time=18809ms".
// So it took 18.8 seconds.

  var StartWork = function()
  {
    console.log("start");
    var startTime = new Date();
    var workCounter=0;
    var sum=0;
    var WorkHard = function()
    {
      if(workCounter>=2000) 
      {
        var ms = (new Date()).getTime() - startTime.getTime();
        console.log("done: sum=" + sum + " time=" + ms + "ms"); 
        return;
      }
      setTimeout(WorkHard,0);
      for(var i=0; i<1500000; i++) {sum++;}
      workCounter++;
    };
    WorkHard();
  };

// Now, as we planned, we move the setTimeout to before the difficult part
// This prints: "done: sum=3000000000 time=12680ms"
// So it took 12.6 seconds. With a little math, (18.8-12.6)/2000 = 3.1ms
// We have effectively shaved off 3.1ms of the original 4.5ms of dead time.
// Assuming some of that time may be attributed to function calls and variable 
// instantiations, we have eliminated the wait time imposed by setTimeout.

// LESSON LEARNED: If you want to use the setTimeout(Func,0) trick with high 
// performance in mind, make sure your function takes more than 4.5ms, and set 
// the next timeout at the start of your function, instead of the end.
///////////////////////////////////////////////


//////// setTimeout(Func,0) Step 3 ////////////
// The results of Step 2 are very educational, but it doesn't really tell us how to apply the
// concept to the real world.  Step 2 says "make sure your function takes more than 4.5ms".
// No one makes functions that take 4.5ms. Functions either take a few microseconds, 
// or several seconds, or several minutes. This magic 4.5ms is unattainable.

// To solve the problem, we introduce the concept of "Burn Time".
// Lets assume that you can break up your difficult function into pieces that take 
// a few milliseconds or less to complete. Then the concept of Burn Time says, 
// "crunch several of the individual pieces until we reach 4.5ms, then exit"

// Step 1 shows a function that is asyncronous, but takes 9 seconds to run. In reality
// we could have easilly incremented workCounter 2000 times in under a millisecond.
// So, duh, that should not be made asyncronous, its horrible. But what if you don't know
// how many times you need to increment the number, maybe you need to run the loop 20 times,
// maybe you need to run the loop 2 billion times.

  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  for(var i=0; i<2000000000; i++) // 2 billion
  {
    workCounter++;
  }
  var ms = (new Date()).getTime() - startTime.getTime();
  console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 

// prints: "done: workCounter=2000000000 time=7214ms"
// So it took 7.2 seconds. Can we break this up into smaller pieces? Yes.
// I know, this is a retarded example, bear with me.

  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  var each = function()
  {
    workCounter++;
  };
  for(var i=0; i<20000000; i++) // 20 million
  {
    each();
  }
  var ms = (new Date()).getTime() - startTime.getTime();
  console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 

// The easiest way is to break it up into 2 billion smaller pieces, each of which take 
// only several picoseconds to run. Ok, actually, I am reducing the number from 2 billion
// to 20 million (100x less).  Just adding a function call increases the complexity of the loop
// 100 fold. Good lesson for some other topic.
// prints: "done: workCounter=20000000 time=7648ms"
// So it took 7.6 seconds, thats a good starting point.
// Now, lets sprinkle in the async part with the burn concept

  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  var index=0;
  var end = 20000000;
  var each = function()
  {
    workCounter++;
  };
  var Work = function()
  {
    var burnTimeout = new Date();
    burnTimeout.setTime(burnTimeout.getTime() + 4.5); // burnTimeout set to 4.5ms in the future
    while((new Date()) < burnTimeout)
    {
      if(index>=end) 
      {
        var ms = (new Date()).getTime() - startTime.getTime();
        console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 
        return;
      }
      each();
      index++;
    }
    setTimeout(Work,0);
  };

// prints "done: workCounter=20000000 time=107119ms"
// Sweet Jesus, I increased my 7.6 second function to 107.1 seconds.
// But it does prevent the browser from locking up, So i guess thats a plus.
// Again, the actual objective here is just to increment workCounter, so the overhead of all
// the async garbage is huge in comparison. 
// Anyway, Lets start by taking advice from Step 2 and move the setTimeout above the hard part. 

  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  var index=0;
  var end = 20000000;
  var each = function()
  {
    workCounter++;
  };
  var Work = function()
  {
    if(index>=end) {return;}
    setTimeout(Work,0);
    var burnTimeout = new Date();
    burnTimeout.setTime(burnTimeout.getTime() + 4.5); // burnTimeout set to 4.5ms in the future
    while((new Date()) < burnTimeout)
    {
      if(index>=end) 
      {
        var ms = (new Date()).getTime() - startTime.getTime();
        console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 
        return;
      }
      each();
      index++;
    }
  };

// This means we also have to check index right away because the last iteration will have nothing to do
// prints "done: workCounter=20000000 time=52892ms"  
// So, it took 52.8 seconds. Improvement, but way slower than the native 7.6 seconds.
// The Burn Time is the number you tweak to get a nice balance between native loop speed
// and browser responsiveness. Lets change it from 4.5ms to 50ms, because we don't really need faster
// than 50ms gui response.

  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  var index=0;
  var end = 20000000;
  var each = function()
  {
    workCounter++;
  };
  var Work = function()
  {
    if(index>=end) {return;}
    setTimeout(Work,0);
    var burnTimeout = new Date();
    burnTimeout.setTime(burnTimeout.getTime() + 50); // burnTimeout set to 50ms in the future
    while((new Date()) < burnTimeout)
    {
      if(index>=end) 
      {
        var ms = (new Date()).getTime() - startTime.getTime();
        console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 
        return;
      }
      each();
      index++;
    }
  };

// prints "done: workCounter=20000000 time=52272ms"
// So it took 52.2 seconds. No real improvement here which proves that the imposed limits of setTimeout
// have been eliminated as long as the burn time is anything over 4.5ms
///////////////////////////////////////////////


//////// setTimeout(Func,0) Step 4 ////////////
// The performance numbers from Step 3 seem pretty grim, but GUI responsiveness is often worth it.
// Here is a short library that embodies these concepts and gives a descent interface.

  var WilkesAsyncBurn = function()
  {
    var Now = function() {return (new Date());};
    var CreateFutureDate = function(milliseconds)
    {
      var t = Now();
      t.setTime(t.getTime() + milliseconds);
      return t;
    };
    var For = function(start, end, eachCallback, finalCallback, msBurnTime)
    {
      var i = start;
      var Each = function()
      {
        if(i==-1) {return;} //always does one last each with nothing to do
        setTimeout(Each,0);
        var burnTimeout = CreateFutureDate(msBurnTime);
        while(Now() < burnTimeout)
        {
          if(i>=end) {i=-1; finalCallback(); return;}
          eachCallback(i);
          i++;
        }
      };
      Each();
    };
    var ForEach = function(array, eachCallback, finalCallback, msBurnTime)
    {
      var i = 0;
      var len = array.length;
      var Each = function()
      {
        if(i==-1) {return;}
        setTimeout(Each,0);
        var burnTimeout = CreateFutureDate(msBurnTime);
        while(Now() < burnTimeout)
        {
          if(i>=len) {i=-1; finalCallback(array); return;}
          eachCallback(i, array[i]);
          i++;
        }
      };
      Each();
    };

    var pub = {};
    pub.For = For;          //eachCallback(index); finalCallback();
    pub.ForEach = ForEach;  //eachCallback(index,value); finalCallback(array);
    WilkesAsyncBurn = pub;
  };

///////////////////////////////////////////////


//////// setTimeout(Func,0) Step 5 ////////////
// Here is an examples of how to use the library from Step 4.

  WilkesAsyncBurn(); // Init the library
  console.log("start");
  var startTime = new Date();
  var workCounter=0;
  var FuncEach = function()
  {
    if(workCounter%1000==0)
    {
      var s = "<div></div>";
      var div = jQuery("*[class~=r1]");
      div.append(s);
    }
    workCounter++;
  };
  var FuncFinal = function()
  {
    var ms = (new Date()).getTime() - startTime.getTime();
    console.log("done: workCounter=" + workCounter + " time=" + ms + "ms"); 
  };
  WilkesAsyncBurn.For(0,2000000,FuncEach,FuncFinal,50);

// prints: "done: workCounter=20000000 time=149303ms"
// Also appends a few thousand divs to the html page, about 20 at a time.
// The browser is responsive the entire time, mission accomplished

// LESSON LEARNED: If your code pieces are super tiny, like incrementing a number, or walking through 
// an array summing the numbers, then just putting it in an "each" function is going to kill you. 
// You can still use the concept here, but your "each" function should also have a for loop in it 
// where you burn a few hundred items manually.  
///////////////////////////////////////////////

2

Đưa ra một hàm worker không đồng bộ someFunctionsẽ gọi lại một hàm kết quả với một resultđối số cho biết vòng lặp có nên tiếp tục hay không:

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

Để kiểm tra xem có kết thúc vòng lặp hay không, hàm worker someFunctioncó thể chuyển tiếp hàm kết quả tới các hoạt động không đồng bộ khác. Ngoài ra, toàn bộ biểu thức có thể được đóng gói thành một hàm không đồng bộ bằng cách lấy một hàm donelàm hàm gọi lại.


1

Nếu bạn thích câu trả lời của wilsonpage nhưng quen với việc sử dụng cú pháp của async.js, đây là một biến thể:

function asyncEach(iterableList, callback, done) {
  var i = -1,
      length = iterableList.length;

  function loop() {
      i++;
      if (i === length) {
        done(); 
        return;
      }
      callback(iterableList[i], loop);
  } 
  loop();
}


asyncEach(['A', 'B', 'C'], function(item, callback) {
    setTimeout(function(){
    document.write('Iteration ' + item + ' <br>');
    callback();
  }, 1000);
}, function() {
  document.write('All done!');
});

Demo có thể được tìm thấy tại đây - http://jsfiddle.net/NXTv7/8/


1

Đây là một ví dụ khác mà tôi nghĩ dễ đọc hơn những ví dụ khác, trong đó bạn bọc hàm không đồng bộ của mình bên trong một hàm nhận một donehàm, chỉ mục vòng lặp hiện tại và kết quả (nếu có) của lệnh gọi không đồng bộ trước đó:

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

Sau khi done()được gọi, nó sẽ kích hoạt cuộc gọi không đồng bộ tiếp theo, một lần nữa chuyển vào hàm done, chỉ mục hiện tại và kết quả trước đó. Khi toàn bộ vòng lặp được hoàn thành, vòng lặp đã cung cấp callbacksẽ được gọi.

Đây là một đoạn mã bạn có thể chạy:

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
}


1

Bạn có thể sử dụng được async awaitgiới thiệu trong ES7:

for ( /* ... */ ) {
    let result = await someFunction(param1, param2);
}
alert("For cycle ended");

Điều này chỉ hoạt động nếu someFunctiontrả lại một Lời hứa!

Nếu someFunctionkhông trả lại Lời hứa, thì bạn có thể tự mình trả lại Lời hứa như sau:

function asyncSomeFunction(param1,praram2) {
  return new Promise((resolve, reject) => {
    someFunction(praram1,praram2,(result)=>{
      resolve(result);
    })
  })
}

Sau đó thay thế dòng này await someFunction(param1, param2);bằngawait asynSomeFunction(param1, param2);

Hãy hiểu lời hứa trước khi viết async awaitmã!


Điều này sẽ cho Unexpected await inside loop.
Reyraa

@Reyraa đó không phải là javascriptvấn đề. Cảnh báo đó xuất hiện từ eslintcấu hình của bạn . Tôi luôn luôn vô hiệu hóa các quy tắc từ eslintvì ở hầu hết các nơi tôi thực sự cần phải chờ đợi bên trong vòng lặp
Praveena

0

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

BIÊN TẬP:

liên kết từ github: 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;
}

Chức năng này cho phép bạn tạo phần trăm ngắt trong vòng lặp for bằng cách sử dụng settings.limit. Thuộc tính giới hạn chỉ là một số nguyên, nhưng khi được đặt là array.length * 0.1, điều này sẽ làm cho settings.limit_callback được gọi sau mỗi 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
 */

ví dụ:

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

0

Một giải pháp dựa trên thư viện hứa hẹn:

/*
    Since this is an open question for JS I have used Kris Kowal's Q promises for the same
*/

var Q = require('q');
/*
    Your LOOP body
    @success is a parameter(s) you might pass
*/
var loopBody = function(success) {
    var d = Q.defer(); /* OR use your favorite promise library like $q in angular */
    /*
        'setTimeout' will ideally be your node-like callback with this signature ... (err, data) {}
        as shown, on success you should resolve 
        on failure you should reject (as always ...) 
    */
    setTimeout(function(err, data) {
        if (!err) {
            d.resolve('success');
        } else {
            d.reject('failure');
        }
    }, 100); //100 ms used for illustration only 
    return d.promise;
};

/*
    function to call your loop body 
*/
function loop(itr, fn) {
    var def = Q.defer();
    if (itr <= 0) {
        def.reject({ status: "un-successful " });
    } else {
        var next = loop.bind(undefined, itr - 1, fn); // 'next' is all there is to this 
        var callback = fn.bind(undefined /*, a, b, c.... */ ); // in case you want to pass some parameters into your loop body
        def.promise = callback().then(def.resolve, next);
    }
    return def.promise;
}
/*
    USAGE: loop(iterations, function(){})
    the second argument has to be thenable (in other words return a promise)
    NOTE: this loop will stop when loop body resolves to a success
    Example: Try to upload file 3 times. HURRAY (if successful) or log failed 
*/

loop(4, loopBody).then(function() {
    //success handler
    console.log('HURRAY')
}, function() {
    //failed 
    console.log('failed');
});

0

Tôi cần gọi một số Xlần hàm không đồng bộ , mỗi lần lặp phải xảy ra sau khi lần lặp trước đó được thực hiện, vì vậy tôi đã viết một thư viện nhỏ có thể được sử dụng như sau:

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

Mỗi lần gọi hàm vòng lặp do người dùng xác định, nó có hai đối số, chỉ số lặp và giá trị trả về cuộc gọi trước đó.

Đây là một ví dụ về đầu ra:

"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"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.