명시 적 약속 건설 반 패턴은 무엇이며 어떻게 방지합니까?


516

나는 다음과 같은 것을하는 코드를 작성하고있었습니다.

function getStuffDone(param) {           | function getStuffDone(param) {
    var d = Q.defer(); /* or $q.defer */ |     return new Promise(function(resolve, reject) {
    // or = new $.Deferred() etc.        |     // using a promise constructor
    myPromiseFn(param+1)                 |         myPromiseFn(param+1)
    .then(function(val) { /* or .done */ |         .then(function(val) {
        d.resolve(val);                  |             resolve(val);
    }).catch(function(err) { /* .fail */ |         }).catch(function(err) {
        d.reject(err);                   |             reject(err);
    });                                  |         });
    return d.promise; /* or promise() */ |     });
}                                        | }

누군가 나에게 이것을 " 지연된 안티 패턴 "또는 " Promise생성자 안티 패턴 "이라고하는데,이 코드에서 나쁜 점은 무엇이며 왜 안티 패턴 이라고 불리는가 ?


이것을 제거하는 것이 (왼쪽이 아닌 오른쪽의 컨텍스트에서) getStuffDone함수 래퍼를 제거 하고 Promise 리터럴을 사용하는지 확인할 수 있습니까?
Dembinski

1
또는 래퍼의 catch블록에 getStuffDone반 패턴이 있습니까?
Dembinski

1
최소한 네이티브 Promise예제의 경우 .thenand .catch핸들러에 불필요한 함수 래퍼도 있습니다 (예 : 그냥있을 수 .then(resolve).catch(reject)있습니다). 안티 패턴의 완벽한 폭풍.
노아 프레이 타스

6
@NoahFreitas 그 코드는 교훈적인 목적으로 그렇게 작성되었습니다. 다음과 같은 코드를 많이 읽은
후이 문제를 겪는

명시적인 Promise 구성뿐만 아니라 전역 변수 사용을 제거하는 방법에 대해서는 stackoverflow.com/questions/57661537/… 도 참조하십시오 .
David Spector

답변:


357

연기 안티 패턴 (현재 명시 적 건설 안티 패턴) 에 의해 만들어 낸 Esailija 내가 먼저 약속을 사용할 때 약속하기에 새로운 일반적인 안티 패턴 사람들입니다, 내가 직접했습니다. 위의 코드의 문제점은 체인을 약속하는 사실을 활용하지 못한다는 것입니다.

약속은 .then연결될 수 있으며 약속을 직접 반환 할 수 있습니다. 코드 getStuffDone를 다음과 같이 다시 작성할 수 있습니다.

function getStuffDone(param){
    return myPromiseFn(param+1); // much nicer, right?
}

약속은 비동기 코드를 더 읽기 쉽게 만들고 그 사실을 숨기지 않고 동기 코드처럼 동작하는 것입니다. 약속은 한 번의 연산 값에 대한 추상화를 나타내며, 프로그래밍 언어로 된 문장이나 표현의 개념을 추상화합니다.

API를 약속으로 변환하고 이를 자동으로 수행 할 수 없거나 이러한 방식으로 표현하기 쉬운 집계 함수를 작성할 때만 지연된 오브젝트를 사용해야합니다 .

Esailija 인용 :

이것은 가장 일반적인 안티 패턴입니다. 약속을 실제로 이해하지 못하고 약속을 영광스러운 이벤트 이미 터 또는 콜백 유틸리티로 생각할 때 여기에 빠지기 쉽습니다. 요점은 비동기 코드가 플랫 들여 쓰기 및 하나의 예외 채널과 같은 동기 코드의 손실 된 속성을 대부분 유지하도록하는 것입니다.


@ BenjaminGruenbaum : 나는 이것을 위해 연기 된 것을 사용한다고 확신하므로 새로운 질문이 필요하지 않습니다. 방금 답변에서 누락 된 유스 케이스라고 생각했습니다. 내가하고있는 일은 집계의 반대와 비슷해 보이지 않습니까?
mhelvens

1
@mhelvens 콜백이 아닌 API를 수동으로 "콜백 API를 약속으로 변환"부분에 맞는 promise API로 분할하는 경우. 반 패턴은 정당한 이유없이 다른 약속으로 약속을 포장하는 것에 관한 것입니다. 약속을 포장하지 않으므로 여기에 적용되지 않습니다.
Benjamin Gruenbaum

@ BenjaminGruenbaum : 아, 비록 연기 된 것은 스스로 안티 패턴으로 간주되었지만 블루 버드가 더 이상 사용하지 않는 것과 "API를 약속으로 변환"(이것은 시작으로 약속을 감싸지 않은 경우)을 언급 한 것입니다.
mhelvens

@ mhelvens 초과 지연된 안티 패턴이 실제로 수행하는 작업에 더 정확할 것입니다. Bluebird .defer()는 새로운 약속 약속 생성자로 API를 더 이상 사용하지 않으며 약속 생성 개념을 더 이상 사용하지 않습니다 :)
Benjamin Gruenbaum

1
@ Roamer-1888에게 감사의 말을 전하면 내 문제가 무엇인지 알아내는 데 도움이되었습니다. 나는 그것을 깨닫지 않고 중첩 된 (회복되지 않은) 약속을 만들고있는 것처럼 보입니다.
ghuroo

134

무슨 일이야?

그러나 패턴이 작동합니다!

운이 좋다 불행히도, 일부 경우를 잊어 버렸을 가능성이 높습니다. 내가 본 사건의 절반 이상에서 저자는 오류 처리기를 처리하는 것을 잊었습니다.

return new Promise(function(resolve) {
    getOtherPromise().then(function(result) {
        resolve(result.property.example);
    });
})

다른 약속이 거부되면, 이것은 새로운 약속 (처리 될 곳)으로 전파되는 대신 눈에 띄지 않게됩니다. 새로운 약속은 영원히 보류되어 누출을 유발할 수 있습니다.

콜백 코드에서 오류가 발생하는 경우에도 마찬가지입니다 (예 : result가없고 property예외가 발생한 경우). 그것은 처리되지 않고 새 약속을 해결하지 않은 채로 둡니다.

반대로, using .then()은 이러한 두 시나리오를 자동으로 처리하고 오류가 발생하면 새로운 약속을 거부합니다.

 return getOtherPromise().then(function(result) {
     return result.property.example;
 })

지연된 반 패턴은 번거로울뿐만 아니라 오류가 발생하기 쉽습니다 . .then()체인에 사용 하는 것이 훨씬 안전합니다.

그러나 나는 모든 것을 처리했습니다!

정말? 좋은. 그러나 특히 취소 또는 메시지 전달과 같은 다른 기능을 지원하는 약속 라이브러리를 사용하는 경우 매우 상세하고 풍부합니다. 아니면 미래에 또는 라이브러리를 더 나은 라이브러리로 바꾸고 싶습니까? 이를 위해 코드를 다시 작성하고 싶지 않을 것입니다.

라이브러리의 메소드 ( then)는 모든 기능을 기본적으로 지원할뿐만 아니라 특정 최적화 기능을 갖추고있을 수도 있습니다. 그것들을 사용하면 코드가 더 빨라지거나 최소한 라이브러리의 개정판으로 최적화 될 수 있습니다.

어떻게 피할 수 있습니까?

따라서 수동으로 작성 Promise하거나 Deferred이미 존재하는 약속이 포함되어 있으면 라이브러리 API를 먼저 확인하십시오 . 하지만 - 이연 안티 패턴은 종종 [전용] 관찰자 패턴으로 약속을 볼 사람들에 의해 적용되는 약속이 콜백 이상을 : 그들이 해야하는 작성 가능합니다. 모든 양질의 라이브러리에는 다루기 싫은 모든 하위 수준의 자료를 처리하면서 모든 생각할 수있는 방식으로 약속을 구성하는 데 사용하기 쉬운 많은 기능이 있습니다.

기존 헬퍼 함수에서 지원하지 않는 새로운 방식으로 일부 약속을 작성해야하는 경우, 피할 수없는 지연으로 고유 한 함수를 작성하는 것이 마지막 옵션이어야합니다. 보다 유용한 라이브러리로 전환하거나 현재 라이브러리에 버그를 신고하십시오. 관리자는 기존 기능에서 컴포지션을 도출하고, 새로운 도우미 기능을 구현하고 /하거나 처리해야하는 에지 사례를 식별 할 수 있어야합니다.


를 포함하는 함수 이외의 setTimeout생성자가 사용될 수 있지만 "Promise constructor anitpattern"으로 간주되지 않는 예가 있습니까?
guest271314

1
@ guest271314 : 약속을 반환하지 않는 모든 비동기. 종종 충분하지만 도서관의 약속 약속 도우미로 더 나은 결과를 얻을 수 있습니다. 그리고 항상 가장 낮은 수준에서 약속해야합니다. 따라서 ""을 포함한 기능이setTimeout 아니라 " 기능 setTimeout자체 "입니다.
Bergi

"그리고 항상 가장 낮은 수준에서 약속해야한다. 그래서"포함하는 기능이 setTimeout아니라 "기능 setTimeout자체" "라고 설명 할 수있다.
guest271314

@ guest271314 호출 만 포함하는 함수는 함수 자체setTimeout분명히 다릅니다. 그렇지 않습니까? setTimeout
Bergi

4
지금까지 명확하게 언급되지 않은 중요한 교훈 중 하나는 Promise와 그 체인 'then'이 하나의 비동기 작업을 나타냅니다. 초기 작업은 Promise 생성자에 있고 최종 종점은 ' 그런 다음 '기능. 따라서 동기화 작업 다음에 비동기 작업이있는 경우 동기화 항목을 약속에 넣습니다. 비동기 작업 후 동기화가있는 경우 동기화 항목을 'then'에 넣으십시오. 첫 번째 경우 원래 약속을 반환하십시오. 두 번째 경우 Promise / then 체인 (Promise)도 반환하십시오.
David Spector
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.