'return await promise'와 'return promise'의 차이점


108

아래 코드 샘플을 감안할 때 동작에 차이가 있습니까? 그렇다면 그 차이점은 무엇입니까?

return await promise

async function delay1Second() {
  return (await delay(1000));
}

return promise

async function delay1Second() {
  return delay(1000);
}

내가 이해했듯이 첫 번째는 비동기 함수 내에서 오류 처리가 가능하며 오류는 비동기 함수의 Promise에서 버블 링됩니다. 그러나 두 번째는 한 번 더 적은 틱이 필요합니다. 이 올바른지?

이 스 니펫은 참조를 위해 Promise를 반환하는 일반적인 함수입니다.

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

3
네가 내 의미를 오해하고 내가 궁금해하는 것에 실제로 대답하지 않았기 때문에 내 질문을 편집했습니다.
PitaJ

1
@PitaJ : async두 번째 ( return promise) 샘플 에서 를 제거하려고했다고 생각합니다 .
Stephen Cleary

1
@PitaJ :이 경우 두 번째 예제는 promise로 해결 된 promise를 반환합니다. 오히려 이상합니다.
Stephen Cleary

5
jakearchibald.com/2017/await-vs-return-vs-return-await 는 차이점을 요약 한 멋진 기사입니다
sanchit

2
@StephenCleary, 나는 이것을 우연히 만났고 처음에는 정확히 똑같은 생각을했습니다. 약속으로 해결 된 약속은 여기서 의미가 없습니다. 이 회전하지만, promise.then(() => nestedPromise)평평 것하고 "따라" nestedPromise. C #의 중첩 작업과 다른 점이 흥미 롭습니다 Unwrap. 참고로, 단순히 await somePromise 호출 Promise.resolve(somePromise).then하는 것이 아니라 somePromise.then몇 가지 흥미로운 의미 차이 가있는 것으로 보입니다 .
noseratio dec

답변:


155

대부분의 경우 return와 사이에는 눈에 띄는 차이가 없습니다 return await. 두 버전 모두 delay1Second똑같은 관찰 가능한 동작 을 가지고 있습니다 (그러나 구현에 따라 return await중간 Promise객체가 생성 될 수 있으므로 버전이 약간 더 많은 메모리를 사용할 수 있음).

그러나 @PitaJ가 지적했듯이 차이가있는 경우가 있습니다. returnor return awaittry- catch블록에 중첩 된 경우 입니다. 이 예를 고려하십시오

async function rejectionWithReturnAwait () {
  try {
    return await Promise.reject(new Error())
  } catch (e) {
    return 'Saved!'
  }
}

async function rejectionWithReturn () {
  try {
    return Promise.reject(new Error())
  } catch (e) {
    return 'Saved!'
  }
}

첫 번째 버전에서 비동기 함수는 결과를 반환하기 전에 거부 된 promise를 기다립니다. 이로 인해 거부가 예외로 바뀌고 catch절에 도달하게됩니다. 따라서이 함수는 문자열 "Saved!"로 해결되는 promise를 반환합니다.

그러나 함수의 두 번째 버전은 async 함수 내에서 기다리지 않고 거부 된 promise를 직접 반환합니다. 즉, catch케이스가 호출 되지 않고 호출자가 대신 거부를받습니다.


스택 추적이 다를 것이라고 언급 할 수도 있습니다 (try / catch 없이도)? 나는이 예에서 가장 자주로 실행 문제 사람들의 생각 :]
벤자민 Gruenbaum

한 시나리오에서 루프 return new Promise(function(resolve, reject) { })내에서 사용 하고 a 후 루프 내에서 for...of호출 resolve()하면 pipe()파이프가 완료 될 때까지 프로그램 실행이 일시 중지되지 않지만 원하는대로 사용하는 것으로 나타났습니다 await new Promise(...). 후자는 유효하거나 올바른 구문입니까? 에 대한 '속기' return await new Promise(...)입니까? 후자가 작동하고 전자가 작동하지 않는 이유를 이해하도록 도와 주시겠습니까? 컨텍스트를 들어, 시나리오에 solution 02이 답변
user1063287

12

다른 답변에서 언급했듯이 약속을 직접 반환하여 버블 링 할 때 약간의 성능 이점이있을 수 있습니다. 그 이유는 결과를 먼저 기다린 다음 다른 약속으로 다시 래핑 할 필요가 없기 때문입니다. 그러나 아직 테일 콜 최적화에 대해 이야기 한 사람은 없습니다 .

테일 호출 최적화 또는 "적절한 테일 호출" 은 인터프리터가 호출 스택을 최적화하는 데 사용하는 기술입니다. 현재는 기술적으로 ES6 표준의 일부 임에도 불구 하고 아직 많은 런타임이 지원 하지는 않지만 향후 지원이 추가 될 수 있으므로 현재 좋은 코드를 작성하여 준비 할 수 있습니다.

간단히 말해서 TCO (또는 PTC) 는 다른 함수에서 직접 반환하는 함수에 대한 새 프레임을 열지 않음 으로써 호출 스택을 최적화합니다 . 대신 동일한 프레임을 재사용합니다.

async function delay1Second() {
  return delay(1000);
}

에서 delay()직접 반환 되므로 delay1Second()PTC를 지원하는 런타임은 먼저 delay1Second()(외부 함수)에 대한 프레임을 열지 만 (내부 함수)에 대해 다른 프레임을 여는 대신 delay()외부 함수에 대해 열린 동일한 프레임을 재사용합니다. 이것은 예를 들어, 매우 큰 재귀 함수 로 스택 오버플로 (hehe)를 방지 할 수 있기 때문에 스택을 최적화합니다 fibonacci(5e+25). 본질적으로 그것은 훨씬 더 빠른 루프가됩니다.

PTC는 내부 함수가 직접 반환되는 경우에만 활성화됩니다 . 함수의 결과가 반환되기 전에 변경 될 때 사용되지 않습니다 (예 : return (delay(1000) || null), 또는 return await delay(1000).

그러나 내가 말했듯이 대부분의 런타임과 브라우저는 아직 PTC를 지원하지 않으므로 지금은 큰 차이를 만들지 못하지만 코드의 미래 보장에는 해를 끼칠 수 없습니다.

이 질문에서 더 많은 것을 읽으십시오 : Node.js : 비동기 함수에서 꼬리 호출을위한 최적화가 있습니까?


2

babel실제로 트랜스 파일러 (아마도 )가 실제로 렌더링 하는 방식에 따라 다르기 때문에 대답하기 어려운 질문 async/await입니다. 분명한 것 :

  • 첫 번째 구현 Promise 체인에서 하나가 적을 있지만 두 구현은 동일하게 작동해야합니다 .

  • 특히 불필요한을 삭제하면 await두 번째 버전은 트랜스 파일러의 추가 코드가 필요하지 않지만 첫 번째 버전은 필요합니다.

따라서 코드 성능 및 디버깅 관점에서 두 번째 버전이 더 바람직하지만 아주 약간만 그렇지만 첫 번째 버전은 약속을 반환한다는 것을 명확하게 표시한다는 점에서 약간의 가독성 이점이 있습니다.


함수가 동일하게 작동하는 이유는 무엇입니까? 첫 번째는 확인 된 값 ( undefined)을 반환하고 두 번째는 Promise.
Amit

4
@Amit 두 함수 모두 약속을 반환합니다
PitaJ

Ack. 이것이 내가 참을 수없는 async/await이유입니다. 추론하기가 훨씬 더 어렵습니다. @PitaJ가 정확하고 두 함수 모두 Promise를 반환합니다.
nrabinowitz

두 비동기 함수의 본문을 try-catch? 이 return promise경우 어떤 것도 rejection잡히지 않고 정확하지만, return await promise경우에는 그렇지 않습니까?
PitaJ

둘 다 Promise를 반환하지만 첫 번째는 원시 값을 "약속"하고 두 번째는 Promise를 "약속"합니다. await이들 각각이 일부 콜 사이트에 있다면 결과는 매우 다를 것입니다.
Amit

1

눈에 띄는 차이점 : 약속 거부는 다른 장소에서 처리됩니다.

  • return somePromisesomePromise 는 콜 사이트에 전달 하고 await somePromise 는 콜 사이트 (있는 경우)에 정산합니다. 따라서 somePromise가 거부되면 로컬 catch 블록이 처리하지 않고 호출 사이트의 catch 블록이 처리합니다.

async function foo () {
  try {
    return Promise.reject();
  } catch (e) {
    console.log('IN');
  }
}

(async function main () {
  try {
    let a = await foo();
  } catch (e) {
    console.log('OUT');
  }
})();
// 'OUT'

  • return await somePromise지역에 정착 하겠다는 약속 을 먼저 기다릴 것입니다 . 따라서 값 또는 예외가 먼저 로컬에서 처리됩니다. => somePromise거부 되면 로컬 catch 블록이 실행됩니다 .

async function foo () {
  try {
    return await Promise.reject();
  } catch (e) {
    console.log('IN');
  }
}

(async function main () {
  try {
    let a = await foo();
  } catch (e) {
    console.log('OUT');
  }
})();
// 'IN'

이유 : return await Promise로컬 및 외부 모두를 return Promise기다립니다.

세부 단계 :

약속 반환

async function delay1Second() {
  return delay(1000);
}
  1. 전화 delay1Second();
const result = await delay1Second();
  1. 내부 delay1Second()에서 함수 delay(1000)[[PromiseStatus]]: 'pending. 그것을라고 부르 자 delayPromise.
async function delay1Second() {
  return delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
  1. 비동기 함수는 반환 값을 Promise.resolve()( Source ) 안에 래핑합니다 . delay1Second비동기 함수 이기 때문에 다음 이 있습니다.
const result = await Promise.resolve(delayPromise); 
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
  1. Promise.resolve(delayPromise)delayPromise입력이 이미 약속이기 때문에 아무것도하지 않고 반환합니다 ( MDN Promise.resolve 참조 ).
const result = await delayPromise; 
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
  1. awaitdelayPromise가 정착 될 때까지 기다립니다 .
  • delayPromisePromiseValue = 1로 충족되는 IF :
const result = 1; 
  • ELSE는 delayPromise거부됩니다.
// jump to catch block if there is any

반환 대기 약속

async function delay1Second() {
  return await delay(1000);
}
  1. 전화 delay1Second();
const result = await delay1Second();
  1. 내부 delay1Second()에서 함수 delay(1000)[[PromiseStatus]]: 'pending. 그것을라고 부르 자 delayPromise.
async function delay1Second() {
  return await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
  1. 현지 대기는 delayPromise정착 될 때까지 기다립니다 .
  • 사례 1 : delayPromisePromiseValue = 1로 충족됩니다.
async function delay1Second() {
  return 1;
}
const result = await Promise.resolve(1); // let's call it "newPromise"
const result = await newPromise; 
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: 1
const result = 1; 
  • 사례 2 : delayPromise거부 됨 :
// jump to catch block inside `delay1Second` if there is any
// let's say a value -1 is returned in the end
const result = await Promise.resolve(-1); // call it newPromise
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: -1
const result = -1;

용어 사전:

  • 정착 : Promise.[[PromiseStatus]]변경 사항 pendingresolvedrejected

0

여기에 실용적인 코드를 남겨 두었습니다.

 let x = async function () {
  return new Promise((res, rej) => {
    setTimeout(async function () {
      console.log("finished 1");
      return await new Promise((resolve, reject) => { // delete the return and you will see the difference
        setTimeout(function () {
          resolve("woo2");
          console.log("finished 2");
        }, 5000);
      });
      res("woo1");
    }, 3000);
  });
};

(async function () {
  var counter = 0;
  const a = setInterval(function () { // counter for every second, this is just to see the precision and understand the code
    if (counter == 7) {
      clearInterval(a);
    }

    console.log(counter);
    counter = counter + 1;
  }, 1000);
  console.time("time1");
  console.log("hello i starting first of all");
  await x();
  console.log("more code...");
  console.timeEnd("time1");
})();

"x"함수는 다른 함수보다 비동기 함수일뿐입니다. 반환 값을 삭제하면 "more code ..."가 인쇄됩니다.

변수 x는 다른 비동기 함수를 갖는 비동기 함수일뿐입니다. 코드의 메인에서 변수 x의 함수를 호출하기 위해 대기를 호출합니다. 완료되면 코드 시퀀스를 따르며 이는 정상입니다. "async / await"에 대해, 그러나 x 함수 내부에는 또 다른 비동기 함수가 있습니다. 이것은 promise를 반환하거나 "promise를 반환합니다."x 함수 안에 머물러 메인 코드를 잊어 버립니다. 즉, "console.log ("more code .. "), 반면에 우리가 넣으면"await "모든 기능이 완료 될 때까지 기다렸다가 마지막으로 메인 코드의 정상적인 순서를 따릅니다.

"console.log ("completed 1 "delete the"return ") 아래에 동작이 표시됩니다.


1
이 코드가 문제를 해결할 수 있지만 문제를 해결하는 방법과 이유에 대한 설명포함 하여 게시물의 품질을 향상시키는 데 실제로 도움이되며 더 많은 찬성 투표가 발생할 수 있습니다. 지금 질문하는 사람뿐만 아니라 미래에 독자를 위해 질문에 답하고 있다는 것을 기억하십시오. 제발 편집 설명을 추가하고 제한 및 가정이 적용 무엇의 표시를 제공하는 답변을.
Brian

0

다음은 "return await"가 필요하다는 것을 실행하고 확신 할 수있는 타이프 스크립트 예제입니다.

async function  test() {
    try {
        return await throwErr();  // this is correct
        // return  throwErr();  // this will prevent inner catch to ever to be reached
    }
    catch (err) {
        console.log("inner catch is reached")
        return
    }
}

const throwErr = async  () => {
    throw("Fake error")
}


void test().then(() => {
    console.log("done")
}).catch(e => {
    console.log("outer catch is reached")
});

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