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

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

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

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

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

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

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



답변

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

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

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

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


답변

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

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"
})


답변

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

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

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
  });
}


답변

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

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
}


답변

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

체인 분할

두 함수 모두 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 () 를 사용할 수 있어야합니다 .


답변

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

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);
});


답변

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 */);