[javascript] 비동기 콜백 함수 세트를 어떻게 기다릴 수 있습니까?

자바 스크립트에서 다음과 같은 코드가 있습니다.

forloop {
    //async call, returns an array to its callback
}

이러한 비동기 호출이 모두 완료된 후 모든 배열에 대한 최소값을 계산하고 싶습니다.

그들 모두를 어떻게 기다릴 수 있습니까?

지금 내 유일한 아이디어는 done이라는 부울 배열을 만들고 i 번째 콜백 함수에서 done [i]를 true로 설정 한 다음 while (모두 완료되지 않음) {}

편집 : 가능하지만 추악한 해결책은 각 콜백에서 done 배열을 편집 한 다음 각 콜백에서 다른 모든 완료가 설정된 경우 메서드를 호출하는 것이므로 완료 할 마지막 콜백은 계속되는 메서드를 호출합니다.

미리 감사드립니다.



답변

코드에 대해 구체적이지 않았으므로 시나리오를 작성하겠습니다. 10 개의 ajax 호출이 있고 그 10 개의 ajax 호출의 결과를 누적하고 싶은데 모두 완료되면 무언가를하고 싶다고 가정 해 보겠습니다. 배열에 데이터를 축적하고 마지막 항목이 언제 완료되었는지 추적하여 이렇게 할 수 있습니다.

수동 카운터

var ajaxCallsRemaining = 10;
var returnedData = [];

for (var i = 0; i < 10; i++) {
    doAjax(whatever, function(response) {
        // success handler from the ajax call

        // save response
        returnedData.push(response);

        // see if we're done with the last ajax call
        --ajaxCallsRemaining;
        if (ajaxCallsRemaining <= 0) {
            // all data is here now
            // look through the returnedData and do whatever processing 
            // you want on it right here
        }
    });
}

참고 : 여기에서 오류 처리가 중요합니다 (Ajax 호출 방법에 따라 다르기 때문에 표시되지 않음). 하나의 ajax 호출이 오류로 인해 완료되지 않거나 오랜 시간 동안 멈춰 있거나 오랜 시간 후에 시간 초과되는 경우를 어떻게 처리 할 것인지 생각하고 싶을 것입니다.


jQuery 약속

2014 년 제 답변에 추가합니다. 요즘에는 jQuery가 $.ajax()이미 약속을 반환하고 약속 $.when()그룹이 모두 해결되고 반환 결과를 수집 할 때 알려주기 때문에 이러한 유형의 문제를 해결하는 데 약속이 자주 사용됩니다 .

var promises = [];
for (var i = 0; i < 10; i++) {
    promises.push($.ajax(...));
}
$.when.apply($, promises).then(function() {
    // returned data is in arguments[0][0], arguments[1][0], ... arguments[9][0]
    // you can process it here
}, function() {
    // error occurred
});

ES6 표준 약속

kba의 답변에 지정된대로 : 네이티브 promise가 내장 된 환경 (최신 브라우저 또는 node.js 또는 babeljs transpile 사용 또는 promise polyfill 사용)이있는 경우 ES6 지정 약속을 사용할 수 있습니다. 브라우저 지원 은 이 표 를 참조하십시오 . 약속은 IE를 제외한 거의 모든 현재 브라우저에서 지원됩니다.

경우 doAjax()약속 반환, 당신은이 작업을 수행 할 수 있습니다

var promises = [];
for (var i = 0; i < 10; i++) {
    promises.push(doAjax(...));
}
Promise.all(promises).then(function() {
    // returned data is in arguments[0], arguments[1], ... arguments[n]
    // you can process it here
}, function(err) {
    // error occurred
});

약속이없는 비동기 작업을 약속을 반환하는 작업으로 만들어야하는 경우 다음과 같이 “약속”할 수 있습니다.

function doAjax(...) {
    return new Promise(function(resolve, reject) {
        someAsyncOperation(..., function(err, result) {
            if (err) return reject(err);
            resolve(result);
        });
    });
}

그런 다음 위의 패턴을 사용하십시오.

var promises = [];
for (var i = 0; i < 10; i++) {
    promises.push(doAjax(...));
}
Promise.all(promises).then(function() {
    // returned data is in arguments[0], arguments[1], ... arguments[n]
    // you can process it here
}, function(err) {
    // error occurred
});

블루 버드 약속

Bluebird promise 라이브러리와 같이 기능이 더 풍부한 라이브러리를 사용하는 경우 이를 쉽게 수행 할 수 있도록 몇 가지 추가 기능이 내장되어 있습니다.

 var doAjax = Promise.promisify(someAsync);
 var someData = [...]
 Promise.map(someData, doAjax).then(function(results) {
     // all ajax results here
 }, function(err) {
     // some error here
 });


답변

2015 년부터 체크인 : 이제 최신 브라우저 (Edge 12, Firefox 40, Chrome 43, Safari 8, Opera 32 및 Android 브라우저 4.4.4 및 iOS Safari 8.4 에서 기본 약속 이 있지만 Internet Explorer, Opera Mini 및 이전 버전은 제외됨) 안드로이드).

10 개의 비동기 작업을 수행하고 작업이 모두 완료되었을 때 알림을 받으려면 Promise.all외부 라이브러리없이 native를 사용할 수 있습니다 .

function asyncAction(i) {
    return new Promise(function(resolve, reject) {
        var result = calculateResult();
        if (result.hasError()) {
            return reject(result.error);
        }
        return resolve(result);
    });
}

var promises = [];
for (var i=0; i < 10; i++) {
    promises.push(asyncAction(i));
}

Promise.all(promises).then(function AcceptHandler(results) {
    handleResults(results),
}, function ErrorHandler(error) {
    handleError(error);
});


답변

when 메소드 와 함께 jQuery의 Deferred 객체를 사용할 수 있습니다 .

deferredArray = [];
forloop {
    deferred = new $.Deferred();
    ajaxCall(function() {
      deferred.resolve();
    }
    deferredArray.push(deferred);
}

$.when(deferredArray, function() {
  //this code is called after all the ajax calls are done
});


답변

다음과 같이 에뮬레이션 할 수 있습니다.

  countDownLatch = {
     count: 0,
     check: function() {
         this.count--;
         if (this.count == 0) this.calculate();
     },
     calculate: function() {...}
  };

각 비동기 호출은 다음을 수행합니다.

countDownLatch.count++;

메서드의 끝에서 각 비동기 호출에서 다음 줄을 추가합니다.

countDownLatch.check();

즉, 카운트 다운 래치 기능을 에뮬레이트합니다.


답변

이것은 제 생각에 가장 깔끔한 방법입니다.

Promise.all

FetchAPI

(어떤 이유로 Array.map이 .then 내부에서 작동하지 않습니다.하지만 .forEach 및 [] .concat () 또는 이와 유사한 것을 사용할 수 있습니다)

Promise.all([
  fetch('/user/4'),
  fetch('/user/5'),
  fetch('/user/6'),
  fetch('/user/7'),
  fetch('/user/8')
]).then(responses => {
  return responses.map(response => {response.json()})
}).then((values) => {
  console.log(values);
})


답변

다음과 같은 제어 흐름 라이브러리를 사용하십시오. after

after.map(array, function (value, done) {
    // do something async
    setTimeout(function () {
        // do something with the value
        done(null, value * 2)
    }, 10)
}, function (err, mappedArray) {
    // all done, continue here
    console.log(mappedArray)
})


답변