async function 에 new operator 를 사용하는 방법

in #kr-dev4 years ago (edited)

tr;dl

  • async가 필요한 constructor를 만들 때 return (async()=>{ .... })() 로 감싼다.
  • 사용 시 await new ... 와 같이 new 앞에 await 을 붙여 사용한다.

javascript 에서는 function 을 사용자 정의 객체 타입으로 사용하여 객체 인스턴스를 사용할 수 있게 해주는 new 라는 operator가 있다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/new

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

이렇게 정의할 경우

var mycar = new Car("Eagle", "Talon TSi", 1993);

mycar와

var kenscar = new Car("Nissan", "300ZX", 1992);

kenscar는 다른 객체를 만들어서 mycar.year, kenscar.year 가 각각 다른 값을 유지할 수 있다.

개인적으로 function을 좋아하기 때문에 React도 hook이 생기고 난 다음부턴 조금씩 쓰기 시작했고 깊게 파고 들수록 함수가 가지고 있는 유연함과 강력함에 더더욱 빠져들고 있다.

최근 async/await 비동기 패턴을 자주 쓰면서 async function도 자주 사용하는데
객체 인스턴스를 사용하기 위해 new 를 사용하려고 하면 문제가 있는 것을 발견했다.

가령 아래와 같이 function scope 안에서 await를 사용하고

async function foo() {
  let pubInfo = await (await fetch("https://www.cloudflare.com/cdn-cgi/trace")).text();
  return {
    getTrace: ()=> pubInfo
  }
}

이와 같이 async function을 만들고 난 후
bar라는 변수를 new를 가지고 foo객체로 만드려고

let bar = new foo();

하면
Uncaught TypeError: foo is not a constructor 오류가 발생한다.
async 가 붙으면서 function인 foo의 new operator를 사용할 수 없다.

결국 foo 안에서 await 을 사용하려면 어떻게 해야할까?
원하는 것은 결국 constructor 에서 return 으로 반환 받는 { getTrace ... } object를 받으면 되는 것에 집중해보면
function 안의 scope을 전부 (async()=> {.... })() 와 같은 IIFE(Immediately Invoked Function Expression)로 감싸서 반환하면 어떨까?

function foo() {
  return (async ()=>{
    let pubInfo = await (await fetch("https://www.cloudflare.com/cdn-cgi/trace")).text();
    return {
      getTrace: ()=> pubInfo
    }
  })();
}

그러면 foo 는 new 로 instantiate 되면서 (async ()=> {})() block을 실행하면서 Promise 타입을 반환할 것이고
이를 다시 await으로 실행하면 되지 않을까?

new foo();
> Promise {<pending>}

new foo() 했을 때 Promise를 반환한다!
자 그럼 await을 붙여서 풀어보자.

bar = await new foo()
{getTrace: ƒ}

객체가 달려오는 걸 확인할 수 있다.
따라서 bar.getTrace() 를 실행해보면

bar.getTrace()
"fl=34f135
h=www.cloudflare.com
ip=....
ts=...
visit_scheme=https
uag=....
colo=ICN
http=http/2
loc=...
tls=TLSv1.3
sni=plaintext
warp=off
"

이와 같이 정상적으로 constructor 를 실행했고 그 내용까지 잘 접근하는 모습을 볼 수 있다.

요약:

  • async가 필요한 constructor를 만들 때 return (async()=>{ .... })() 로 감싼다.
  • 사용 시 await new ... 와 같이 new 앞에 await 을 붙여 사용한다.