Promise에서 여러 값을 올바르게 반환하는 방법은 무엇입니까?


87

최근에 특정 상황에 몇 번 부딪 쳤는데, 제대로 해결하는 방법을 몰랐습니다. 다음 코드를 가정하십시오.

somethingAsync()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( amazingData ) {
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
}

나는에 대한 액세스를 갖고 싶어 할 경우 지금은 상황이 발생할 수도 amazingData에서 afterSomethingElse.

한 가지 분명한 해결책은에서 배열 또는 해시를 반환하는 것입니다 afterSomething. 왜냐하면 함수에서 하나의 값만 반환 할 수 있기 때문입니다. 그러나 afterSomethingElse문서화하고 이해하기가 훨씬 쉬워 보이기 때문에 2 개의 매개 변수를 받아들이고 마찬가지로 호출하는 방법이 있는지 궁금합니다 .

나는 Q.spread내가 원하는 것과 비슷한 일을하는이 있기 때문에이 가능성에 대해서만 궁금 합니다.




ES6의 구조 해제 할당이 도움이 될 것입니다. 여기에서
Ravi Teja

답변:


88

함수에서 여러 값을 반환 할 수없는 것처럼 여러 속성으로 promise를 해결할 수 없습니다 . Promise는 개념적으로 시간에 따른 값을 나타내므로 복합 값을 나타낼 수 있지만 약속에 여러 값을 넣을 수는 없습니다.

Promise는 본질적으로 단일 값으로 해결됩니다. 이것은 Q 작동 방식, Promises / A + 사양 작동 방식추상화 작동 방식의 일부입니다.

가장 가까운 방법은 Q.spread배열을 사용 하고 반환하거나 지원되거나 BabelJS와 같은 변환 도구를 사용하려는 경우 ES6 구조화를 사용하는 것입니다.

프라 미스 체인 아래로 컨텍스트를 전달하는 것에 대해서는 Bergi의 우수한 표준을 참조하십시오 .


16
여러 속성을 가진 개체로 해결하는 데있어 문제점은 무엇입니까? 해결에서 여러 값을 얻는 간단한 방법처럼 보입니다.
jfriend00 2015

6
그것은 완벽하게 괜찮습니다
Benjamin Gruenbaum

당신은 또한 가지고 약속을 확장 할 수 있습니다 .spread(): 파랑새이 관련 답에 표시처럼 stackoverflow.com/a/22776850/1624862
케빈 Ghadyani을

Promise.all ()의 동작은 이것과 모순되는 것 같습니다. Promise.all([a, b, c]).then(function(x, y, z) {...})x, y 및 z가 a, b 및 c의 확인 된 값으로 평가되는 모든 최신 Javascript 엔진에서 올바르게 작동합니다. 따라서 언어가 사용자 코드에서 쉽게 (또는 깔끔하게) 할 수 없도록하는 것이 더 정확합니다. .all ()을 사용하여 복잡한 방식이지만 원하는 동작을 얻습니다.
Austin Hemmelgarn

3
@AustinHemmelgarn 그것은 단지 거짓이며 배열로Promise.all 충족 됩니다 . In Promise.all([a,b]).then((a, b) => bundefined입니다. 당신이해야 할 이유의 그 .then(([a, b]) =>destructuring 할당되는
벤자민 Gruenbaum

39

하나의 값만 전달할 수 있지만 다음과 같이 여러 값이있는 배열 일 수 있습니다.

function step1(){
  let server = "myserver.com";
  let data = "so much data, very impresive";
  return Promise.resolve([server, data]);
}

다른 측면에서, 당신은 사용할 수 있습니다 destructuring 개별 값을 얻을 수 ES2015에 대한 표현.

function step2([server, data]){
  console.log(server); // print "myserver.com"
  console.log(data);   // print "so much data, very impresive"
  return Promise.resolve("done");
}

두 약속을 모두 호출하고 연결합니다.

step1()
.then(step2)
.then((msg)=>{
  console.log(msg); // print "done"
})

5
그것은라고 destructuring 하지 "deconstructor"을, 그리고 연산자 아니다 : - /
BERGI

3
Btw, 매개 변수에서 바로 구조 해제를 사용할 수 있습니다 function step2([server, data]) { ….-이렇게하면 암시 적 전역에 할당하지 않아도됩니다. 그리고 예제 에서 생성자가 아닌 returnor를 사용해야합니다 . Promise.resolvenew Promise
Bergi

추천을 위해 @Bergi보다!
Alejandro Silva

20

두 값을 모두 포함하는 객체를 반환 할 수 있습니다. 문제는 없습니다.

또 다른 전략은 가치를 전달하는 대신 클로저를 통해 유지 하는 것입니다.

somethingAsync().then(afterSomething);

function afterSomething(amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}

부분적으로 인라인 된 형식이 아닌 완전히 인라인 된 형식 (동등하고 틀림없이 더 일관성이 있음) :

somethingAsync().then(function (amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}

3
then다른 내부 를 반환해도 괜찮 then습니까? 그것은 아니다 안티 패턴 ?
robe007

@ robe007이 말했듯이 이것은 '콜백 지옥'과 비슷하지 않습니까? 여기에 대신 콜백 함수의 당신의 중첩 다음 블록은이 가지는 약속의 바로 그 목적을 물리 칠 것이다
Dheeraj

5

할 수있는 두 가지, 객체 반환

somethingAsync()
    .then( afterSomething )
    .then( afterSomethingElse );

function processAsync (amazingData) {
     //processSomething
     return {
         amazingData: amazingData, 
         processedData: processedData
     };
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}

function afterSomethingElse( dataObj ) {
    let amazingData = dataObj.amazingData,
        processedData = dataObj.proccessedData;
}

범위를 사용하십시오!

var amazingData;
somethingAsync()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( returnedAmazingData ) {
  amazingData = returnedAmazingData;
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
  //use amazingData here
}

3

여기에 당신이해야한다고 생각하는 방법이 있습니다.

체인 분할

두 함수 모두 amazingData를 사용 하므로 전용 함수에 두는 것이 합리적입니다. 나는 일반적으로 일부 데이터를 재사용하고 싶을 때마다 그렇게하므로 항상 함수 인수로 존재합니다.

귀하의 예제가 일부 코드를 실행 중이므로 모두 함수 내에서 선언되었다고 가정합니다. 나는 그것을 toto () 라고 부를 것 입니다. 그런 다음 afterSomething ()afterSomethingElse () 둘 다 실행하는 또 다른 함수를 갖게됩니다 .

function toto() {
    return somethingAsync()
        .then( tata );
}

일반적으로 Promise를 사용하는 방법이므로 return 문을 추가 했음을 알 수 있습니다. 필요한 경우 계속 연결할 수 있도록 항상 promise를 반환합니다. 여기에서 somethingAsync ()amazingData 를 생성 하고 새 함수 내부의 모든 곳에서 사용할 수 있습니다.

이제이 새 함수는 일반적으로 processAsync ()도 비동기 에 따라 달라집니다 .

비동기가 아닌 processAsync

상황을 지나치게 복잡하게 만들 이유가 없습니다. processAsync () 가 비동기가 아닌 가 없습니다. 오래된 좋은 순차 코드가 그것을 만들 것입니다.

function tata( amazingData ) {
    var processed = afterSomething( amazingData );
    return afterSomethingElse( amazingData, processed );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( amazingData, processedData ) {
}

다음과 같은 경우에는 중요하지 않습니다. afterSomethingElse () 가 비동기 작업을 수행 하지 않습니다. 그렇다면 promise가 반환되고 체인이 계속 될 수 있습니다. 그렇지 않은 경우 결과 값이 반환됩니다. 그러나 함수가 then () 에서 호출되기 때문에 값은 어쨌든 (적어도 원시 Javascript에서) promise로 래핑됩니다.

processAsync 비동기

경우 processAsync ()가 비동기 코드는 약간 다를 것입니다. 여기서 우리는 afterSomething ()afterSomethingElse () 가 다른 곳에서는 재사용되지 않을 것이라고 생각합니다.

function tata( amazingData ) {
    return afterSomething()
        .then( afterSomethingElse );

    function afterSomething( /* no args */ ) {
        return processAsync( amazingData );
    }
    function afterSomethingElse( processedData ) {
        /* amazingData can be accessed here */
    }
}

afterSomethingElse ()의 이전과 동일 . 비동기 일 수도 있고 아닐 수도 있습니다. 약속이 반환되거나 해결 된 약속으로 래핑 된 값이 반환됩니다.


당신의 코딩 스타일은 제가 사용하는 것과 매우 비슷해서 2 년이 지난 후에도 대답했습니다. 나는 모든 곳에서 익명 기능을 갖는 것을 좋아하지 않습니다. 읽기가 어렵습니다. 지역 사회에서 매우 흔하더라도. 우리가 교체 된 것과 같다 콜백 지옥 a로 약속 - 연옥을 .

나는 또한 그때 의 기능의 이름을 유지하고 싶습니다 짧게 . 어쨌든 로컬로만 정의됩니다. 그리고 대부분의 경우 다른 곳에서 정의 된 다른 함수 (재사용 가능)를 호출하여 작업을 수행합니다. 매개 변수가 하나 뿐인 함수에 대해서도 그렇게하므로 함수 시그니처에 매개 변수를 추가 / 제거 할 때 함수를 입력 및 해제 할 필요가 없습니다.

식사 예

다음은 그 예입니다.

function goingThroughTheEatingProcess(plenty, of, args, to, match, real, life) {
    return iAmAsync()
        .then(chew)
        .then(swallow);

        function chew(result) {
            return carefullyChewThis(plenty, of, args, "water", "piece of tooth", result);
        }

        function swallow(wine) {
            return nowIsTimeToSwallow(match, real, life, wine);
        }
}

function iAmAsync() {
    return Promise.resolve("mooooore");
}

function carefullyChewThis(plenty, of, args, and, some, more) {
    return true;
}

function nowIsTimeToSwallow(match, real, life, bobool) {
}

Promise.resolve () 에 너무 집중하지 마십시오 . 해결 된 약속을 만드는 빠른 방법입니다. 이것으로 달성하려는 것은 내가 실행하는 모든 코드를 단일 위치에서 실행하는 것입니다. 그때 . 더 설명적인 이름을 가진 다른 모든 함수는 재사용이 가능합니다.

이 기술의 단점은 많은 기능을 정의한다는 것입니다. 그러나 모든 곳에서 익명의 기능을 사용하지 않으려면 나는 필요한 고통입니다. 어쨌든 위험은 무엇입니까 : 스택 오버플로? (농담!)


다른 답변에 정의 된대로 배열 또는 객체를 사용하는 것도 작동합니다. 이것은 Kevin Reid가 제안한 답변입니다. 입니다.

bind () 또는 Promise.all ()을 사용할 수도 있습니다. . 여전히 코드를 분할해야합니다.

bind 사용

함수를 재사용 가능하게 유지하고 싶지만 내부 내용 매우 짧게 유지할 필요가 없다면 bind ()를 사용할 수 있습니다 .

function tata( amazingData ) {
    return afterSomething( amazingData )
        .then( afterSomethingElse.bind(null, amazingData) );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( amazingData, processedData ) {
}

간단하게하기 위해 bind () 는 함수가 호출 될 때 args 목록 (첫 번째 항목 제외)을 함수 앞에 추가합니다.

Promise.all 사용

귀하의 게시물에서 spread () 사용에 대해 언급했습니다 . 사용중인 프레임 워크를 사용한 적이 없지만 사용 방법은 다음과 같습니다.

어떤 사람들은 Promise.all () 이 모든 문제에 대한 해결책이라고 생각하므로 언급 할 가치가 있다고 생각합니다.

function tata( amazingData ) {
    return Promise.all( [ amazingData, afterSomething( amazingData ) ] )
        .then( afterSomethingElse );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( args ) {
    var amazingData = args[0];
    var processedData = args[1];
}

Promise.all ()에 데이터를 전달할 수 있습니다. 배열의 존재에주의 .하지만 promise가 실패하지 않으면 처리가 중지됩니다.

그리고 args 인자 에서 새로운 변수를 정의하는 대신 모든 종류의 멋진 작업을 위해 then () 대신 spread () 를 사용할 수 있어야합니다 .


3

객체를 만들고 해당 객체에서 인수를 추출하기 만하면됩니다.

let checkIfNumbersAddToTen = function (a, b) {
return new Promise(function (resolve, reject) {
 let c = parseInt(a)+parseInt(b);
 let promiseResolution = {
     c:c,
     d : c+c,
     x : 'RandomString'
 };
 if(c===10){
     resolve(promiseResolution);
 }else {
     reject('Not 10');
 }
});
};

promiseResolution에서 인수를 가져옵니다.

checkIfNumbersAddToTen(5,5).then(function (arguments) {
console.log('c:'+arguments.c);
console.log('d:'+arguments.d);
console.log('x:'+arguments.x);
},function (failure) {
console.log(failure);
});

2

Promise에서 반환하는 것은 무엇이든 다음에 Unwrap 될 약속으로 포장됩니다. .then() 단계 .

다음과 같은 하나 이상의 동기 값과 함께 하나 이상의 promise를 반환해야 할 때 흥미로워집니다.

Promise.resolve([Promise.resolve(1), Promise.resolve(2), 3, 4])
       .then(([p1,p2,n1,n2]) => /* p1 and p2 are still promises */);

이러한 경우에 사용하는 것이 필수적 일 것이다 Promise.all()얻을 p1p2약속은 다음에 풀어 .then()과 같은 단계

Promise.resolve(Promise.all([Promise.resolve(1), Promise.resolve(2), 3, 4]))
       .then(([p1,p2,n1,n2]) => /* p1 is 1, p2 is 2, n1 is 3 and n2 is 4 */);


0

튜플을 반환하기 만하면됩니다.

async add(dto: TDto): Promise<TDto> {
console.log(`${this.storeName}.add(${dto})`);
return firebase.firestore().collection(this.dtoName)
  .withConverter<TDto>(this.converter)
  .add(dto)
  .then(d => [d.update(this.id, d.id), d.id] as [any, string])
  .then(x => this.get(x[1]));

}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.