지연된 배열을 $ .when ()에 전달


447

여기에 무슨 일이 일어나고 있는지에 대한 고안된 예가 있습니다 : http://jsfiddle.net/adamjford/YNGcm/20/

HTML :

<a href="#">Click me!</a>
<div></div>

자바 스크립트 :

function getSomeDeferredStuff() {
    var deferreds = [];

    var i = 1;
    for (i = 1; i <= 10; i++) {
        var count = i;

        deferreds.push(
        $.post('/echo/html/', {
            html: "<p>Task #" + count + " complete.",
            delay: count
        }).success(function(data) {
            $("div").append(data);
        }));
    }

    return deferreds;
}

$(function() {
    $("a").click(function() {
        var deferreds = getSomeDeferredStuff();

        $.when(deferreds).done(function() {
            $("div").append("<p>All done!</p>");
        });
    });
});

나는 "모두 끝났어!" 모든 지연된 작업이 완료된 후에 표시되지만 $.when()지연된 개체 배열을 처리하는 방법을 알지 못하는 것 같습니다. "다됐다!" 배열이 Deferred 객체가 아니기 때문에 먼저 발생하므로 jQuery가 진행되어 방금 완료된 것으로 가정합니다.

나는 객체를 함수에 전달할 수 있다는 것을 알고 $.when(deferred1, deferred2, ..., deferredX)있지만 해결하려는 실제 문제에서 얼마나 많은 지연 객체가 실행 될지 알 수 없습니다.



아래의이 오래된 질문에 대한 새롭고 더 간단한 답변을 추가했습니다. 동일한 결과를 얻기 위해 배열을 사용하거나 전혀 사용할 필요 가 없습니다$.when.apply .
코딩

너무 구체적이기 때문에 질문 주제를 롤백했습니다 (이것은 단지 AJAX 문제가 아닙니다)
Alnitak

답변:


732

에 값의 배열을 전달하려면 어떤 일반적으로 그들에게 별도의 매개 변수를 사용하는 것으로 기대하는 기능 Function.prototype.apply이 경우에 당신이 필요합니다 :

$.when.apply($, my_array).then( ___ );

http://jsfiddle.net/YNGcm/21/ 참조

ES6에서는 ... 스프레드 연산자를 대신 사용할 수 있습니다 .

$.when(...my_array).then( ___ );

두 경우 모두 .then처리기에 필요한 공식 매개 변수의 수를 미리 알지 못할 가능성이 높으므로 arguments해당 약속의 결과를 검색하려면 해당 처리기가 배열 을 처리해야합니다 .


4
이것은 훌륭합니다. :) Google을 통해 이러한 간단한 변경 사항을 처리 할 수 ​​없었습니다.
adamjford

9
그것은하는 일반적인 방법이 아닌 특정 때문에 그건 $.when- f.apply(ctx, my_array)호출 fthis == ctx하고, 설정 인수 내용my_array.
Alnitak

4
@Alnitak : JavaScript를 얼마나 오랫동안 작성했는지를 고려할 때 그 방법에 대해 몰랐습니다.
adamjford

5
FWIW, 전달의 토론과 earler 질문에 엘리의 대답에있는 링크 $null첫 번째 매개 변수로 읽기 가치가있다. 이 특별한 경우에는 문제가되지 않습니다.
Alnitak

4
@Alnitak : 그렇습니다. 그러나 $입력이 적고 구현이 변경 null될 때 안전합니다 $.when(이 경우는 아니지만 this기본적으로 변경되지 않는 이유는 무엇입니까 ).
Tomasz Zieliński

109

(감사합니다!) 위의 해결 방법은 제대로의 연기에 제공되는 객체 돌아 가지의 문제를 해결하지 않는 resolve()jQuery를가 호출 때문에 방법 done()fail()각각의 매개 변수가 아닌 배열과 콜백을. 즉, arguments의사 배열 을 사용하여 지연된 배열에서 반환 된 모든 확인 / 거부 된 개체를 가져와야합니다.

$.when.apply($,deferreds).then(function() {
     var objects=arguments; // The array of resolved objects as a pseudo-array
     ...
};

지연된 배열을 전달 했으므로 결과 배열을 다시 가져 오는 것이 좋습니다. 의사 배열 대신 실제 배열을 다시 가져 와서 같은 메소드를 사용할 수도 있습니다 Array.sort().

다음은 이러한 문제를 해결 하는 when.jswhen.all()방법에서 영감을 얻은 솔루션입니다 .

// Put somewhere in your scripting environment
if (typeof jQuery.when.all === 'undefined') {
    jQuery.when.all = function (deferreds) {
        return $.Deferred(function (def) {
            $.when.apply(jQuery, deferreds).then(
                function () {
                    def.resolveWith(this, [Array.prototype.slice.call(arguments)]);
                },
                function () {
                    def.rejectWith(this, [Array.prototype.slice.call(arguments)]);
                });
        });
    }
}

이제 지연 / 프로 미스 배열을 전달하고 콜백에서 해결 / 거부 된 오브젝트 배열을 다음과 같이 다시 가져올 수 있습니다.

$.when.all(deferreds).then(function(objects) {
    console.log("Resolved objects:", objects);
});

6
당신은 너무 당신이 '이'deferred.resolveWith 같은 원래의 deferreds를 얻을 수 resolveWith 및 rejectWith을 사용할 수 있습니다 (이, [Array.prototype.slice.call (인수)]) 등
제이미 페이트

1
코드에는 작은 문제가 있습니다. 배열에 하나의 요소 만 있으면 결과 배열은 단일 요소가있는 배열 대신 해당 결과 만 반환합니다 (배열을 예상하는 코드가 손상됨). 수정하려면 var toArray = function (args) { return deferreds.length > 1 ? $.makeArray(args) : [args]; }대신 이 기능을 사용하십시오 Array.prototype.slice.call.
루안 니코

흠, 이것은 404를 잡는 것 같지 않습니다.
t.mikael.d

.fail은 .reject 여야하므로 404를 잡을 수 있습니다.
t.mikael.d

38

when메소드를 배열에 적용 할 수 있습니다 .

var arr = [ /* Deferred objects */ ];

$.when.apply($, arr);

일련의 jQuery 연기로 어떻게 작업합니까?


나는 실제로 그 질문을 보았지만 그 질문의 모든 추가 세부 사항이 내 문제에 대한 대답 (바로 거기에 있음)이 내 머리 위로 날아 갔다고 생각합니다.
adamjford

1
@adamjford, 기분이 나아진다면 귀하의 질문을 더 쉽게 소비 할 수 있다는 것을 알았습니다 (먼저 특정 Google 검색 에서이 정확한 문제에 대해).
patridge

@patridge : 도움이되었다 니 다행입니다.
adamjford

이것은 훌륭한 답변이지만 원래 질문의 예에 어떻게 적용되었는지는 분명하지 않았습니다. 연결된 질문을 참조한 후 "$ .when (deferreds) .done (function () {"행을 "$ .when.apply ($, deferreds) .done (function () { ". 맞습니까?
Garland 교황

7

여러 개의 병렬 AJAX 호출을 호출 할 때 각 응답을 처리하기위한 두 가지 옵션이 있습니다.

  1. 동기식 AJAX 호출을 사용하십시오.
  2. Promises'array를 사용 하고 s $.when를 수락 promise하고 .done모든 promise응답이 각 응답으로 성공적으로 반환 되면 콜백 이 호출됩니다 .

function ajaxRequest(capitalCity) {
   return $.ajax({
        url: 'https://restcountries.eu/rest/v1/capital/'+capitalCity,
        success: function(response) {
        },
        error: function(response) {
          console.log("Error")
        }
    });
}
$(function(){
   var capitalCities = ['Delhi', 'Beijing', 'Washington', 'Tokyo', 'London'];
   $('#capitals').text(capitalCities);

   function getCountryCapitals(){ //do multiple parallel ajax requests
      var promises = [];   
      for(var i=0,l=capitalCities.length; i<l; i++){
            var promise = ajaxRequest(capitalCities[i]);
            promises.push(promise);
      }
  
      $.when.apply($, promises)
        .done(fillCountryCapitals);
   }
  
   function fillCountryCapitals(){
        var countries = [];
        var responses = arguments;
        for(i in responses){
            console.dir(responses[i]);
            countries.push(responses[i][0][0].nativeName)
        }  
        $('#countries').text(countries);
   }
  
   getCountryCapitals()
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
  <h4>Capital Cities : </h4> <span id="capitals"></span>
  <h4>Respective Country's Native Names : </h4> <span id="countries"></span>
</div>


1
귀하의 답변이 초과되어 질문 제목을 수정 한 내용도 마찬가지입니다. OP는 이미 AJAX 호출을 수행하고 지연된 객체의 배열을 얻는 방법을 알고있었습니다. 질문 의 유일한 요점은 해당 배열을에 전달하는 방법이었습니다 $.when.
Alnitak

5
사용 가능한 옵션을 사용하여 예제를 통해 자세하게 설명하는 것이 더 좋을 것이라고 생각했으며 downvote가 필요하다고 생각하지 않았습니다.
vinayakj 2009 년

2
downvote는 1. 동기화 제안 (권장하지는 않지만) 2. 예제의 품질이 좋지 않은 코드 ( for ... in어레이에 포함?)
Alnitak

1
1. 동의 함, (not recommended)동의하지 않아야 함 2. 동의 for ... in하지 않음 - 배열에 필요한 속성 (추가 속성 없음) 만 포함되어 있으므로 괜찮습니다. 고맙습니다 어쨌든
vinayakj

1
다시 : 2-문제는 보증을 할 수 없거나 추가하기에 멍청한 다른 사람들이 복사 할 수 있다는 것입니다 Array.prototype. 어쨌든 성능이 중요하지 않은 코드의 경우 작업 완료 .map와 같이 for/ push루프 대신 사용 하는 것이 좋습니다 var promises = capitalCities.map(ajaxRequest); $.when.apply($, promises).then(fillCountryCapitals).
Alnitak

6

필요없는 간단한 대안으로 $.when.apply또는 array여러 병렬 약속에 대한 하나의 약속을 생성하기 위해 다음과 같은 패턴을 사용할 수 있습니다 :

promise = $.when(promise, anotherPromise);

예 :

function GetSomeDeferredStuff() {
    // Start with an empty resolved promise (or undefined does the same!)
    var promise;
    var i = 1;
    for (i = 1; i <= 5; i++) {
        var count = i;

        promise = $.when(promise,
        $.ajax({
            type: "POST",
            url: '/echo/html/',
            data: {
                html: "<p>Task #" + count + " complete.",
                delay: count / 2
            },
            success: function (data) {
                $("div").append(data);
            }
        }));
    }
    return promise;
}

$(function () {
    $("a").click(function () {
        var promise = GetSomeDeferredStuff();
        promise.then(function () {
            $("div").append("<p>All done!</p>");
        });
    });
});

노트:

  • 나는 누군가 체인을 사용하여 순차적으로 약속을보고 나서 이것을 알아 냈습니다. promise = promise.then(newpromise)
  • 단점은 씬 뒤에 여분의 약속 객체를 만들고 끝에 전달 된 매개 변수는 유용하지 않습니다 (추가 객체 안에 중첩되어 있기 때문에). 짧고 간단하지만 원하는 것을 위해.
  • 단점은 어레이 또는 어레이 관리가 필요하지 않다는 것입니다.

2
내가 틀렸다면 나를 수정하십시오. 그러나 귀하의 접근 방식은 $ .when ($ .when ($ .when (...)))를 효과적으로 중첩하므로 10 반복이 있으면 10 레벨 깊이로 재귀 적으로 중첩됩니다. 자체 약속을 반환하기 전에 각 수준에서 자식의 중첩 된 약속을 반환하기를 기다려야하므로 병렬로 보이지 않습니다. 수용 된 답변의 배열 방식은 유연한 매개 변수 동작을 사용하기 때문에 훨씬 깨끗하다고 ​​생각합니다 $ .when () 메소드.
Anthony McLin

@AnthonyMcLin : then()비슷한 방식으로 체인 호출 과 마찬가지로 더 나은 성능 (대부분의 비동기 코딩에서는 무시할 수있는)이 아니라 코딩에 대한 간단한 대안을 제공하기위한 것입니다. 동작 $.when은 병렬 (연쇄되지 않음) 인 것처럼 동작 하는 것입니다. 그것이 작동하는 것처럼 유용한 대안을 버리기 전에 시도하십시오 :)
사라짐 코딩

2
@Alnitak : 코스 말. 당신은 확실히 의견을 가질 자격이 있지만, 당신은 분명히 이것을 직접 사용하지 않았습니다. 내 자신의 의견은이 기술의 실제 사용을 기반으로합니다. 그것은 작동 하고 사용하기 때문에 왜 "로드의 경고"(하나)와 과장을 기반으로 도구 상자에서 도구를 버리고 "아무것도 해결하지 않습니다"(사실이 아님)-배열 처리를 제거하고 반환 위치에서 병렬 약속 체인을 단순화합니다. 병렬 처리 사례에서는 거의 사용되지 않는 값이 필요하지 않습니다. Downvotes는 :) "이 답변이 도움이되지 않습니다"에 대한 있어야된다
코딩 사라

1
@GoneCoding 안녕하세요. 귀하의 답변에 투표 논평을 추가하지 않도록 요청할 수 있습니까? 주석에는 적합하지만 그렇지 않으면 좋은 콘텐츠에서 산만 해지는 것은 잡음입니다. 감사.
10

1
@halfer : 더 이상 게시하지 않지만 원본에 표시된 무지에 짜증이납니다. 요즘 나에게 모든 새로운 아이디어를 유지 :)
사라 코딩

4

$ .each를 사용하여 다른 것을 제안하고 싶습니다.

  1. 우리는 다음과 같이 아약스 함수를 선언 할 수있다 :

    function ajaxFn(someData) {
        this.someData = someData;
        var that = this;
        return function () {
            var promise = $.Deferred();
            $.ajax({
                method: "POST",
                url: "url",
                data: that.someData,
                success: function(data) {
                    promise.resolve(data);
                },
                error: function(data) {
                    promise.reject(data);
                }
            })
            return promise;
        }
    }
  2. 우리가 보낼 아약스로 함수 배열을 생성하는 코드의 일부 :

    var arrayOfFn = [];
    for (var i = 0; i < someDataArray.length; i++) {
        var ajaxFnForArray = new ajaxFn(someDataArray[i]);
        arrayOfFn.push(ajaxFnForArray);
    }
  3. 그리고 아약스를 전송하여 함수를 호출 :

    $.when(
        $.each(arrayOfFn, function(index, value) {
            value.call()
        })
    ).then(function() {
            alert("Cheer!");
        }
    )

1

코드를 변환하고 ES6에 액세스 할 수있는 경우 객체의 반복 가능한 각 항목을 개별 인수로 구체적으로 적용하는 스프레드 구문을 $.when()필요에 따라 사용할 수 있습니다.

$.when(...deferreds).done(() => {
    // do stuff
});

MDN 링크-확산 구문


0

angularJS 또는 Q promise 라이브러리의 변형을 사용하는 .all()경우이 정확한 문제를 해결 하는 방법이 있습니다.

var savePromises = [];
angular.forEach(models, function(model){
  savePromises.push(
    model.saveToServer()
  )
});

$q.all(savePromises).then(
  function success(results){...},
  function failed(results){...}
);

전체 API를 참조하십시오 :

https://github.com/kriskowal/q/wiki/API-Reference#promiseall

https://docs.angularjs.org/api/ng/service/$q


4
이것은 전혀 관련이 없습니다.
Benjamin Gruenbaum

@BenjaminGruenbaum 어떻게 그렇게? 모든 javascript promise 라이브러리는 비슷한 API를 공유하며 다른 구현을 보여주는 데 아무런 문제가 없습니다. 나는이 페이지에 도달하여 각도에 대한 답을 찾았으며 많은 다른 사용자 가이 페이지에 도달 할 것이라고 생각하며 반드시 jquery 전용 환경에 있지는 않습니다.
mastaBlasta

2
즉, jQuery의 약속 이 API를 공유 하지 않기 때문에 이는 스택 오버플로에 대한 답변으로 완전히 부적절합니다. Angular에 대한 비슷한 답변이 있으며 거기에 요청할 수 있습니다. (물론 .map여기 있지만 오 잘해야합니다).
Benjamin Gruenbaum

0

각 루프에 게시 한 다음 아약스에서받은 숫자의 일부 필드에서 html 마크 업을 설정하는 경우와 매우 비슷한 경우가있었습니다. 그런 다음이 필드의 (현재 업데이트 된) 값의 합계를 수행하고 총 필드에 배치해야했습니다.

따라서 문제는 모든 숫자에 대한 합계를 시도했지만 비동기 ajax 호출에서 아직 데이터가 도착하지 않았다는 것입니다. 코드를 재사용하려면 몇 가지 기능으로이 기능을 완료해야했습니다. 내 외부 함수는 데이터를 기다린 다음 완전히 업데이트 된 DOM으로 작업을 수행합니다.

    // 1st
    function Outer() {
        var deferreds = GetAllData();

        $.when.apply($, deferreds).done(function () {
            // now you can do whatever you want with the updated page
        });
    }

    // 2nd
    function GetAllData() {
        var deferreds = [];
        $('.calculatedField').each(function (data) {
            deferreds.push(GetIndividualData($(this)));
        });
        return deferreds;
    }

    // 3rd
    function GetIndividualData(item) {
        var def = new $.Deferred();
        $.post('@Url.Action("GetData")', function (data) {
            item.html(data.valueFromAjax);
            def.resolve(data);
        });
        return def;
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.