[javascript] 모든 비동기 forEach 콜백이 완료된 후의 콜백

제목에서 알 수 있듯이 어떻게해야합니까?

whenAllDone()forEach-loop가 각 요소를 통과하고 비동기 처리를 한 후에 호출하고 싶습니다 .

[1, 2, 3].forEach(
  function(item, index, array, done) {
     asyncFunction(item, function itemDone() {
       console.log(item + " done");
       done();
     });
  }, function allDone() {
     console.log("All done");
     whenAllDone();
  }
);

이처럼 작동하도록 할 수 있습니까? forEach의 두 번째 인수가 모든 반복을 거친 후에 실행되는 콜백 함수 인 경우?

예상 출력 :

3 done
1 done
2 done
All done!



답변

Array.forEach 이 근사함을 제공하지는 않지만 (원한다면) 여러 가지 방법으로 원하는 것을 달성 할 수 있습니다.

간단한 카운터 사용

function callback () { console.log('all done'); }

var itemsProcessed = 0;

[1, 2, 3].forEach((item, index, array) => {
  asyncFunction(item, () => {
    itemsProcessed++;
    if(itemsProcessed === array.length) {
      callback();
    }
  });
});

(@vanuan 및 기타 사용자에게 감사)이 방법은 “완료”콜백을 호출하기 전에 모든 항목이 처리되도록합니다. 콜백에서 업데이트되는 카운터를 사용해야합니다. 비동기 작업의 반환 순서가 보장되지 않으므로 index 매개 변수의 값에 따라 동일한 보장이 제공되지 않습니다.

ES6 약속 사용

(약속 라이브러리는 구형 브라우저에 사용할 수 있습니다) :

  1. 동기 실행을 보장하는 모든 요청 처리 (예 : 1, 2, 3)

    function asyncFunction (item, cb) {
      setTimeout(() => {
        console.log('done with', item);
        cb();
      }, 100);
    }
    
    let requests = [1, 2, 3].reduce((promiseChain, item) => {
        return promiseChain.then(() => new Promise((resolve) => {
          asyncFunction(item, resolve);
        }));
    }, Promise.resolve());
    
    requests.then(() => console.log('done'))
  2. “동기”실행없이 모든 비동기 요청을 처리합니다 (2가 1보다 빠르게 완료 될 수 있음)

    let requests = [1,2,3].map((item) => {
        return new Promise((resolve) => {
          asyncFunction(item, resolve);
        });
    })
    
    Promise.all(requests).then(() => console.log('done'));

비동기 라이브러리 사용

비동기식 이 가장 많이 사용되는 다른 비동기 라이브러리가 있으며 원하는 것을 표현하는 메커니즘을 제공합니다.

편집하다


질문의 본문은 이전의 동기 예제 코드를 제거하도록 편집되었으므로 명확히하기 위해 답변을 업데이트했습니다. 원래 예제는 동기식 코드를 사용하여 비동기 동작을 모델링하므로 다음이 적용되었습니다.

array.forEach이다 동기 등이다 res.write단순히 foreach 문에 전화 후 콜백을 넣을 수 있도록 :

  posts.foreach(function(v, i) {
    res.write(v + ". index " + i);
  });

  res.end();


답변

비동기 함수가 발생하고 코드를 실행하기 전에 작업이 완료되도록하려면 항상 콜백 기능을 사용할 수 있는지 확인하십시오.

예를 들면 다음과 같습니다.

var ctr = 0;
posts.forEach(function(element, index, array){
    asynchronous(function(data){
         ctr++;
         if (ctr === array.length) {
             functionAfterForEach();
         }
    })
});

참고 : 각 functionAfterForEach작업이 완료된 후 실행되는 기능입니다. asynchronousforeach 내에서 실행되는 비동기 함수입니다.


답변

이것이 문제를 해결하기를 바랍니다. 내가 비동기 작업으로 forEach를 실행해야 할 때 일반적 으로이 작업을 수행합니다.

foo = [a,b,c,d];
waiting = foo.length;
foo.forEach(function(entry){
      doAsynchronousFunction(entry,finish) //call finish after each entry
}
function finish(){
      waiting--;
      if (waiting==0) {
          //do your Job intended to be done after forEach is completed
      }
}

function doAsynchronousFunction(entry,callback){
       //asynchronousjob with entry
       callback();
}


답변

비동기식에 몇 개의 오답이 주어 졌는지는 이상합니다. 사례에 ! 인덱스 검사가 예상되는 동작을 제공하지 않는다는 것을 간단히 나타낼 수 있습니다.

// INCORRECT
var list = [4000, 2000];
list.forEach(function(l, index) {
    console.log(l + ' started ...');
    setTimeout(function() {
        console.log(index + ': ' + l);
    }, l);
});

산출:

4000 started
2000 started
1: 2000
0: 4000

를 확인하면 index === array.length - 1첫 번째 요소가 아직 보류중인 동안 첫 번째 반복이 완료되면 콜백이 호출됩니다!

비동기와 같은 외부 라이브러리를 사용하지 않고이 문제를 해결하려면 각 반복 후에 목록의 길이를 줄이고 감소시키는 것이 가장 좋습니다. 스레드가 하나뿐이므로 경쟁 조건이 없을 것입니다.

var list = [4000, 2000];
var counter = list.length;
list.forEach(function(l, index) {
    console.log(l + ' started ...');
    setTimeout(function() {
        console.log(index + ': ' + l);
        counter -= 1;
        if ( counter === 0)
            // call your callback here
    }, l);
});


답변

ES2018을 사용하면 비동기 반복자를 사용할 수 있습니다.

const asyncFunction = a => fetch(a);
const itemDone = a => console.log(a);

async function example() {
  const arrayOfFetchPromises = [1, 2, 3].map(asyncFunction);

  for await (const item of arrayOfFetchPromises) {
    itemDone(item);
  }

  console.log('All done');
}


답변

Promise가없는 솔루션 (다음 작업이 시작되기 전에 모든 작업이 종료되도록 보장) :

Array.prototype.forEachAsync = function (callback, end) {
        var self = this;

        function task(index) {
            var x = self[index];
            if (index >= self.length) {
                end()
            }
            else {
                callback(self[index], index, self, function () {
                    task(index + 1);
                });
            }
        }

        task(0);
    };


    var i = 0;
    var myArray = Array.apply(null, Array(10)).map(function(item) { return i++; });
    console.log(JSON.stringify(myArray));
    myArray.forEachAsync(function(item, index, arr, next){
      setTimeout(function(){
        $(".toto").append("<div>item index " + item + " done</div>");
        console.log("action " + item + " done");
        next();
      }, 300);
    }, function(){
        $(".toto").append("<div>ALL ACTIONS ARE DONE</div>");
        console.log("ALL ACTIONS ARE DONE");
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="toto">

</div>


답변

 var counter = 0;
 var listArray = [0, 1, 2, 3, 4];
 function callBack() {
     if (listArray.length === counter) {
         console.log('All Done')
     }
 };
 listArray.forEach(function(element){
     console.log(element);
     counter = counter + 1;
     callBack();
 });