jQuery 연기 및 약속-.then () 대 .done ()


473

jQuery 지연 및 약속에 대해 읽었 으며 성공적인 콜백에 .then()& .done()를 사용하는 것의 차이점을 볼 수 없습니다 . 나도 에릭 Hynds는 것을 언급 .done().success()같은 기능에 매핑 그러나 나는 그렇게 추측하고있어 .then()모든 콜백 모든 성공적인 작업의 완료에 호출한다.

누구든지 올바른 사용법을 가르쳐 줄 수 있습니까?


15
2016 년 6 월에 출시 된 JQuery 3.0이 Promises / A + 및 ES2015 Promises 사양을 준수하는 첫 번째 버전이라는 점에 유의하십시오. 그 이전의 구현에는 약속이 무엇을 제공해야하는지에 대한 비 호환성이있었습니다.
Flimm

나는 언제 무엇을 사용 해야하는지에 대한 개선 된 권장 사항으로 답변 을 업데이트했습니다 .
Robert Siemer

답변:


577

done()연기 된 문제가 해결되면 연결된 콜백이 시작 됩니다. fail()지연된 항목이 거부되면 연결된 콜백이 시작 됩니다.

jQuery 1.8 이전에는 then()구문 설탕이었습니다.

promise.then( doneCallback, failCallback )
// was equivalent to
promise.done( doneCallback ).fail( failCallback )

1.8부터는 새로운 약속 then()의 별칭 pipe()이며 반환합니다 . 에 대한 자세한 내용 은 여기 를 참조 하십시오pipe() .

success()그리고 error()온에서만 사용할 수 있습니다 jqXHR호출에 의해 반환 된 객체 ajax(). 그들은을위한 간단한 별칭 done()fail()각각 :

jqXHR.done === jqXHR.success
jqXHR.fail === jqXHR.error

또한 done()단일 콜백으로 제한되지 않으며 비 기능을 필터링합니다 (버전 1.8의 문자열에 1.8.1에서 수정해야하는 버그가 있음).

// this will add fn1 to 7 to the deferred's internal callback list
// (true, 56 and "omg" will be ignored)
promise.done( fn1, fn2, true, [ fn3, [ fn4, 56, fn5 ], "omg", fn6 ], fn7 );

동일합니다 fail().


8
then새로운 약속을 돌려주는 것은 내가 놓친 핵심이었습니다. 왜 같은 체인 $.get(....).done(function(data1) { return $.get(...) }).done(function(data2) { ... })data2정의되지 않은 채 실패 했는지 이해할 수 없었습니다 . 원래 약속에 더 많은 처리기를 연결하는 대신 약속을 함께 파이프하기를 원했기 때문에 변경 done으로 then인해 효과가있었습니다.
wrschneider

5
jQuery 3.0은 Promises / A + 및 ES2015 사양을 준수하는 첫 번째 버전입니다.
Flimm

4
나는 왜 내가 왜 다른 것을 사용하는지 이해하지 못합니다. 나는 아약스 전화를 걸 내가 그 전화가 완전히 (의미 reponse 서버에서 반환) 완료 될 때까지 나는 또 다른 아약스 호출을 호출하기 전에 기다릴 필요가 내가 사용한다면 donethen? 왜?
CodingYoshi

@CodingYoshi 마지막으로 해당 질문에 답변 하려면 내 답변 을 확인하십시오 (use .then()).
Robert Siemer

413

반환 결과가 처리되는 방식에도 차이가 있습니다 (연결이라고 함, 연결 done하지 않고 then호출 체인 을 생성 함)

promise.then(function (x) { // Suppose promise returns "abc"
    console.log(x);
    return 123;
}).then(function (x){
    console.log(x);
}).then(function (x){
    console.log(x)
})

다음과 같은 결과가 기록됩니다.

abc
123
undefined

동안

promise.done(function (x) { // Suppose promise returns "abc"
    console.log(x);
    return 123;
}).done(function (x){
    console.log(x);
}).done(function (x){
    console.log(x)
})

다음을 얻을 것이다 :

abc
abc
abc

---------- 업데이트 :

Btw. 원자 유형 값 대신 Promise를 반환하면 외부 약속이 내부 약속이 해결 될 때까지 기다립니다.

promise.then(function (x) { // Suppose promise returns "abc"
    console.log(x);
    return $http.get('/some/data').then(function (result) {
        console.log(result); // suppose result === "xyz"
        return result;
    });
}).then(function (result){
    console.log(result); // result === xyz
}).then(function (und){
    console.log(und) // und === undefined, because of absence of return statement in above then
})

이러한 방식으로 다음과 같은 병렬 또는 순차적 비동기 작업을 작성하는 것이 매우 간단 해집니다.

// Parallel http requests
promise.then(function (x) { // Suppose promise returns "abc"
    console.log(x);

    var promise1 = $http.get('/some/data?value=xyz').then(function (result) {
        console.log(result); // suppose result === "xyz"
        return result;
    });

    var promise2 = $http.get('/some/data?value=uvm').then(function (result) {
        console.log(result); // suppose result === "uvm"
        return result;
    });

    return promise1.then(function (result1) {
        return promise2.then(function (result2) {
           return { result1: result1, result2: result2; }
        });
    });
}).then(function (result){
    console.log(result); // result === { result1: 'xyz', result2: 'uvm' }
}).then(function (und){
    console.log(und) // und === undefined, because of absence of return statement in above then
})

위의 코드는 두 개의 http 요청을 병렬로 발행하여 요청을 더 빨리 완료하는 반면, http 요청 아래에서 순차적으로 실행되므로 서버로드가 줄어 듭니다.

// Sequential http requests
promise.then(function (x) { // Suppose promise returns "abc"
    console.log(x);

    return $http.get('/some/data?value=xyz').then(function (result1) {
        console.log(result1); // suppose result1 === "xyz"
        return $http.get('/some/data?value=uvm').then(function (result2) {
            console.log(result2); // suppose result2 === "uvm"
            return { result1: result1, result2: result2; };
        });
    });
}).then(function (result){
    console.log(result); // result === { result1: 'xyz', result2: 'uvm' }
}).then(function (und){
    console.log(und) // und === undefined, because of absence of return statement in above then
})

121
done결과가 then바뀌는 결과에 아무런 영향을 미치지 않는 개념에 +1 . 다른 사람들이 놓친 거대한 점.
Shanimal

9
그것은의 행동 때문에 가치가 아마이 적용되는 jQuery를 어떤 버전의 대한 언급의 then1.8 변경
bradley.ayers

4
+1 지점으로 직진. 내가 생성 된 실행 가능한 예제를 사람이 혼합 무엇 체인보고 싶어하는 경우 donethen결과를 호출합니다.
마이클 Kropat에게

7
위의 예는 또한 '완료'가 처음 작성된 원래 약속 오브젝트에서 작동하지만 '그럼'은 새 약속을 리턴 함을 강조합니다.
Pulak Kanti Bhattacharyya

2
이것은 jQuery 1.8 이상에 적용됩니다. 이전 버전은 done예제 와 동일하게 작동합니다 . 변화 thenpipe의 사전 1.8은 1.8 이상받을 then행동을.
David Harkness

57

.done() 콜백이 하나 뿐이며 성공 콜백입니다.

.then() 성공과 실패 콜백이 모두 있습니다

.fail() 하나의 실패 콜백이 있습니다

그래서 당신이해야 할 일은 당신에게 달려 있습니다 ... 성공했는지 아니면 실패하는지 걱정하십니까?


18
'then'은 콜 체인을 생성한다고 언급하지 않습니다. Lu4의 답변을 참조하십시오.
oligofren

귀하의 답변은 2011 년입니다 ... 요즘의 반환 값은와 then()다릅니다 done(). 로 then()자주 / 기억 알 수있는 중요한 것은보다 상세하게는 만 포인트 콜백 성공으로 오히려입니다라고합니다. (jQuery 3.0 이전의 상황을 말할 수 없음)
Robert Siemer

14

deferred.done ()

Deferred가 해결 될 때만 호출 핸들러를 추가합니다 . 호출 할 콜백을 여러 개 추가 할 수 있습니다.

var url = 'http://jsonplaceholder.typicode.com/posts/1';
$.ajax(url).done(doneCallback);

function doneCallback(result) {
    console.log('Result 1 ' + result);
}

위와 같이 쓸 수도 있습니다.

function ajaxCall() {
    var url = 'http://jsonplaceholder.typicode.com/posts/1';
    return $.ajax(url);
}

$.when(ajaxCall()).then(doneCallback, failCallback);

deferred.then ()

Deferred가 해결, 거부 또는 여전히 진행 중일 때 호출 핸들러를 추가합니다 .

var url = 'http://jsonplaceholder.typicode.com/posts/1';
$.ajax(url).then(doneCallback, failCallback);

function doneCallback(result) {
    console.log('Result ' + result);
}

function failCallback(result) {
    console.log('Result ' + result);
}

콜백이 제공되지 않은 경우 ( 즉, 사례를 전혀 캡처 하지 않는 then경우) 게시물이 명확하지 않습니다.failfail
BM

실패 사례는 프로그램의 최상위 레벨에서 포착 할 수있는 예외를 발생시킵니다. JavaScript 콘솔에서 예외를 볼 수도 있습니다.
David Spector 2019 년

10

jQuery의 Deferreds가 Promises의 구현으로 간주되는 한 (jQuery3.0은 실제로 스펙을 적용하려고 시도하는 한) 실제로 중요한 차이가 있습니다.

done / then의 주요 차이점은

  • .done() 항상 자신이 무엇을하든, 무엇을 반환하든 관계없이 항상 같은 약속 / 포장 값을 반환합니다.
  • .then() 항상 새 약속을 반환하며, 전달한 함수에 따라 약속의 내용을 제어 할 책임이 있습니다.

jQuery에서 기본 ES2015 Promises로 변환되는 .done()것은 체인이 "해결"상태 인 경우 값이 함수에 전달된다는 점에서 Promise 체인의 함수 주위에 "탭"구조를 구현하는 것과 비슷합니다. 그러나 그 기능의 결과는 체인 자체에 영향을 미치지 않습니다.

const doneWrap = fn => x => { fn(x); return x };

Promise.resolve(5)
       .then(doneWrap( x => x + 1))
       .then(doneWrap(console.log.bind(console)));

$.Deferred().resolve(5)
            .done(x => x + 1)
            .done(console.log.bind(console));

6이 아닌 5를 기록합니다.

나는 done과 doneWrap을 사용하여 로깅을 수행했다. console.log 함수는 실제로 아무것도 반환하지 않기 때문입니다. 그리고 아무것도 반환하지 않는 함수를 전달하면 어떻게됩니까?

Promise.resolve(5)
       .then(doneWrap( x => x + 1))
       .then(console.log.bind(console))
       .then(console.log.bind(console));

그것은 기록 할 것이다 :

5

찾으시는 주소가 없습니다

어떻게 된 거예요? .then을 사용하고 아무것도 반환하지 않는 함수를 전달했을 때 암시적인 결과는 "정의되지 않음"이었습니다. 물론 다음 정의 된 Promise [undefined]를 다음 then 메소드에 반환하여 undefined를 기록했습니다. 우리가 시작한 원래 가치는 기본적으로 없어졌습니다.

.then()핵심은 함수 구성의 형태입니다. 각 단계의 결과는 다음 단계에서 함수의 인수로 사용됩니다. .done이 "탭"으로 가장 잘 생각되는 이유입니다.-> 실제로 컴포지션의 일부가 아니며 특정 단계에서 값을 들여다보고 해당 값에서 함수를 실행하지만 실제로는 변경하지 않는 것입니다. 어떤 식 으로든 구성.

이것은 근본적인 차이점이며, 기본 약속에 .done 메서드가 구현되어 있지 않은 좋은 이유가 있습니다. 우리는 왜 더 .fail 메소드가 없는지 알아낼 필요가 없습니다. 왜냐하면 .fail / .catch는 더 복잡하기 때문입니다 (즉, .fail / .catch는 베어 값을 반환하는 .dach / .then-> 함수의 미러가 아닙니다 "체재"는.


6

then()항상 어떤 경우에도 호출됩니다. 그러나 매개 변수 전달은 jQuery 버전마다 다릅니다.

jQuery를 1.8 이전에, then()동일done().fail() . 그리고 모든 콜백 함수는 동일한 매개 변수를 공유합니다.

그러나 jQuery 1.8부터 then() 부터는 새로운 약속을 반환하고 값을 반환하면 다음 콜백 함수로 전달됩니다.

다음 예제를 보자.

var defer = jQuery.Deferred();

defer.done(function(a, b){
            return a + b;
}).done(function( result ) {
            console.log("result = " + result);
}).then(function( a, b ) {
            return a + b;
}).done(function( result ) {
            console.log("result = " + result);
}).then(function( a, b ) {
            return a + b;
}).done(function( result ) {
            console.log("result = " + result);
});

defer.resolve( 3, 4 );

jQuery 1.8 이전에 답은

result = 3
result = 3
result = 3

모두 result3이 걸립니다. 그리고then() 함수는 항상 다음 함수 같은 연기 객체를 전달합니다.

그러나 jQuery 1.8 기준으로 결과는 다음과 같아야합니다.

result = 3
result = 7
result = NaN

첫 번째 then()함수는 새로운 promise를 반환하고 값 7 (및 전달할 유일한 매개 변수)이 다음으로 전달 done()되므로 두 번째 done()write result = 7입니다. 두 번째 then()값은 7을 값으로 사용 a하고 두 번째 undefined값은 NaN 매개 변수를 사용하여 새 약속을 반환하고 마지막 은 NaN을 결과로 인쇄합니다.bthen()done()


"then ()은 항상 어떤 경우에도 호출 될 것"을 의미합니다. 사실이 아닙니다. Promise 내부에서 오류가 발생한 경우 then ()은 호출되지 않습니다.
David Spector 2019 년

a jQuery.Deferred()가 여러 값을 수신 할 수 있다는 흥미로운 측면 은 첫 번째 값으로 올바르게 전달 .then()됩니다 .—하지만 조금 이상하지만 ... 다음과 같이 .then()할 수 없습니다. (선택된 인터페이스를 통해 return하나의 값만 반환 할 수 있습니다.) Javascript의 기본 Promise은 그렇게하지 않습니다. (정직하게 말하면 더 일관성이 있습니다.)
Robert Siemer

3

다른 답변에서 찾기가 매우 어려운 응답에 매우 간단한 정신 매핑이 있습니다.


2

사용 만 .then()

이것의 단점은 .done()

  • 연결할 수 없습니다
  • 블록 resolve()호출 (모든 .done()핸들러는 동기식으로 실행 됨)
  • resolve()등록 된 .done()처리기 에서 예외가 발생할 수 있습니다 (!)
  • .done()유예 자의 절반을 죽이는 예외 :
    • 추가 .done()처리기는 자동으로 건너 뜁니다.

나는 예외가 자동으로 무시되지 않도록 .then(oneArgOnly)항상 필요 하다고 생각 .catch()했지만 더 이상 사실이 아닙니다. unhandledrejection이벤트 .then()는 콘솔에서 처리되지 않은 예외를 콘솔에 기록 합니다 (기본값). 매우 합리적입니다! 전혀 사용할 이유가 없습니다 .done().

증명

다음 코드 스 니펫은 다음을 보여줍니다.

  • 모든 .done()핸들러는 시점에서 동기식으로 호출됩니다.resolve()
    • 1, 3, 5, 7로 기록
    • 스크립트가 아래로 떨어지기 전에 기록
  • .done()영향 resolve()발신자의 예외
    • catch를 통해 기록 resolve()
  • 예외는 추가 .done()해결로 약속을 깰
    • 8과 10은 기록되지 않습니다!
  • .then() 이 문제들 중 어느 것도 없다
    • 스레드가 유휴 상태가 된 후 2, 4, 6, 9, 11로 기록
    • (스 니펫 환경이없는 unhandledrejection것 같습니다)

Btw의 예외를 .done()제대로 포착 할 수 없습니다 :의 동기 패턴으로 인해 (라이브러리 코드 일 수 있습니다!) 지연되거나 지연된 이체가 이미 해결 된 경우 범인을 첨부하는 호출 에서 .done()오류가 발생합니다 ..resolve().done()

console.log('Start of script.');
let deferred = $.Deferred();
// deferred.resolve('Redemption.');
deferred.fail(() => console.log('fail()'));
deferred.catch(()=> console.log('catch()'));
deferred.done(() => console.log('1-done()'));
deferred.then(() => console.log('2-then()'));
deferred.done(() => console.log('3-done()'));
deferred.then(() =>{console.log('4-then()-throw');
    throw 'thrown from 4-then()';});
deferred.done(() => console.log('5-done()'));
deferred.then(() => console.log('6-then()'));
deferred.done(() =>{console.log('7-done()-throw');
    throw 'thrown from 7-done()';});
deferred.done(() => console.log('8-done()'));
deferred.then(() => console.log('9-then()'));

console.log('Resolving.');
try {
    deferred.resolve('Solution.');
} catch(e) {
    console.log(`Caught exception from handler
        in resolve():`, e);
}
deferred.done(() => console.log('10-done()'));
deferred.then(() => console.log('11-then()'));
console.log('End of script.');
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh"
crossorigin="anonymous"
></script>


몇 가지 : 1)done 이전에 예외가있는 경우 실행되지 않을 것을 말하고 있습니다. 그러나 왜 자동으로 무시됩니까? 예외가 발생했기 때문에 왜 침묵한다고 말합니까? 2)Deferred API가 매우 잘못 수행 되어 객체를 멸시합니다 . 너무 복잡하고 혼란 스럽다. 여기에있는 코드는 요점을 증명하는 데 도움이되지 않으며 증명하려는 내용에 너무 많은 불필요한 복잡성이 있습니다. 3)done at 인덱스 2, 4 및 6 이 왜 2 차 전에 수행 then됩니까?
코딩 요시

내 나쁜, 나중에 투표 할 가치가있다. 예외에 대한 귀하의 의견은 일반적으로 예외가 작동하는 방식입니다. 한 번 제기되면 코드가 실행되지 않습니다. 또한 jQuery 문서에는 지연이 해결 된 경우에만 실행된다고 명시되어 있습니다.
코딩 요시

@CodingYoshi 상황은 여기에서 다릅니다 : 나는 해결 된 약속 / 지연에 대해서만 이야기하고있었습니다. 나는 나머지 성공 핸들러가 부름받지 않았다고 불평하지 않는다. 그것은 정상이다. 그러나 성공적인 약속에 대해 완전히 다른 성공 처리자가 불려지는 이유는 없습니다. .then()예외가 발생했는지 여부에 관계없이 모두 호출됩니다. 그러나 추가 / 남은 .done()휴식.
Robert Siemer

@CodingYoshi 나는 말할 수 있다면 대답을 크게 개선했습니다. 코드와 텍스트.
Robert Siemer

1

jQuery 3.0비교할 때 예기치 않은 동작으로 이어질 수 있으며 이전 답변에서 언급되지 않은 중요한 차이점이 하나 더 있습니다.

다음 코드를 고려하십시오.

let d = $.Deferred();
d.done(() => console.log('then'));
d.resolve();
console.log('now');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>

이것은 출력됩니다 :

then
now

이제, 교체 done()then()바로 그 조각에서 :

var d = $.Deferred();
d.then(() => console.log('then'));
d.resolve();
console.log('now');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>

출력은 다음과 같습니다.

now
then

따라서 즉시 해결 된 지연의 경우 전달 된 함수 done()는 항상 동기 방식으로 호출되는 반면 전달 된 인수 then()는 비동기 적으로 호출됩니다.

이것은 업그레이드 가이드에 언급 된 것처럼 두 콜백이 동 기적으로 호출되는 이전 jQuery 버전과 다릅니다 .

Promises / A + 준수에 필요한 또 다른 동작 변경은 Deferred .then () 콜백이 항상 비동기 적으로 호출된다는 것입니다. 이전에는 .then () 콜백이 이미 해결되었거나 거부 된 지연에 추가 된 경우 콜백은 즉시 동기식으로 실행되었습니다.


-5

.done()약속 체인을 종료하여 추가 단계를 수행 할 수있는 것은 없습니다. 이것은 아무도 jQuery promise 구현이 처리 할 수없는 예외를 던질 수 있음을 의미합니다. 아무도이를 사용하여 처리 할 수 ​​없기 때문 .fail()입니다.

실제로 약속에 더 많은 단계를 첨부하지 않으려면을 사용해야합니다 .done(). 자세한 내용은 약속을해야하는 이유를 참조하십시오


6
주의! 이 답변은 여러 약속 구현에 맞지만 .done()종료 역할이 없는 jQuery에는 적합 하지 않습니다. 문서에 따르면 "deferred.done ()이 지연된 객체를 반환하므로 추가 .done () 메서드를 포함하여 지연된 객체의 다른 메서드를이 메서드에 연결할 수 있습니다". .fail()언급되지 않았지만 그렇습니다.
Roamer-1888

1
내 나쁜, jQuery를 확인하지 않았다
gleb bahmutov

1
@glebbahmutov-다른 사람들이 혼동하지 않도록이 답변을 삭제해야합니까? 그냥 친절한 제안 :)
Andrey

2
답을 삭제하지 마십시오. 사람들이 오해를 해결하는 데 도움이 될 수 있습니다.
Melissa
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.