JS_제너레이터 asnyc / await

sunjin·2024년 2월 20일
0

Javascript

목록 보기
3/4

🌱 제너레이터가 뭘까요 ?

일반 함수는 하나의 값만을 반환
하지만 제너레이터를 사용하면 여러 개의 값을 필요에 따라 하나씩 반환 할 수 있습니다.

중간에 원하는 부분에서 멈추었다가, 그 부분을 다시 실행할 수 있는 능력을 가진 함수
제너레이터 함수는 화살표 함수를 사용할 수 없다. new 연산자와 함께 생성자 호출도 할수 없다.

🍀 제너레이터 함수

function* genDenFunc(){
  yield 1;
}

애스터리스크(*)의 위치는 일관성 유지를 위해 function 키워드 바로 뒤에 붙이는것을 권장

🍀 제너레이터 객체

제너레이터 함수가 반환한 제너레이터 객체는 이터러블 이면서 동시에 이터레이터 다.

제너레이터 객체는 Symbol.iterator 메서드를 상속받은 이터러블 이면서 value, done 프로퍼티를 갖는 이터레이터 리절트 객체를 반환하는 next 메서드를 소유하는 이터레이터이다.

function* genFunc() {
  yield 1;
  yield 2;
  yield 3;
}

const generator = genFunc();

console.log(Symbol.iterator in generator); //true
console.log("next" in generator); //true
function* generateSequence() {
  yield 1;
  yield 2;
  return 3;
}

let generator = generateSequence();

let one = generator.next();

alert(JSON.stringify(one)); // {value: 1, done: false}

현재로서는 첫 번째 값만 받았으므로 함수 실행은 두 번째 줄에서 멈춤

let two = generator.next();

alert(JSON.stringify(two)); // {value: 2, done: false}

generator.next()를 또 호출하면 실행은 return문에 다다르고 함수가 종료됩니다.

let three = generator.next();

alert(JSON.stringify(three)); // {value: 3, done: true}

마지막 결과인 value:3과 done:true를 통해 이를 확인할 수 있다.

제너레이터가 종료되었기 때문에 지금 상태에선 generator.next()를 호출해도 아무 소용이 없다 generator.next()를 여러번 호출해도 객체 {done: true}가 반환될 뿐이다.


🌱 async/await

제너레이터를 통해 비동기 처리를 하면 코드가 무척 장황하고 가독성이 나빠진다. ES8 에서는 제너레이터보다 간단하고 가독성 좋게 비동기 처리를 동기 처리 처럼 동작하도록 async/await이 도입 되었다

async/await은 프로미스를 기반으로 동작한다.
async/await을 사용하면
프로미스의 then/catch/finally 후속 처리 메서드에 콜백 함수를 후속 처리 할 필요 없이 동기 처리처럼 프로미스를 사용할 수 있다.

프로미스의 후속 처리 메서드 없이 동기 처리처럼 프로미스가 처리 결과를 반환하도록 결과를 구현할 수 있다.

🍀 async 함수

  • await 키워드는 반드시 async 함수 내부에서 사용해야 한다.
  • async 함수는 async 키워드를 사용해 정의하며 언제나 프로미스를 반환한다.
  • async 함수는 암묵적으로 반환값을 resolve하는 프로미스를 반환한다.
// async 함수 선언문
async function foo(n) {
  return n;
}
foo(1).then((v) => console.log(v)); // 1

// async 함수 표현식
const bar = async function (n) {
  return n;
};
bar(2).then((v) => console.log()); // 2

// async 화살표 함수
const baz = async (n) => n;
bar(3).then((v) => console.log(v)); // 3

// async 메서드
const obj = {
  async foo(n) {
    return n;
  },
};
obj.foo(4).then((v) => console.log(v)); // 4

// async 클래스 메서드
class MyClass {
  async bar(n) {
    return n;
  }
}
const myClass = new MyClass();
myClass.bar(5).then((v) => console.log(v)); // 5

클래스의 constructor 메서드는 async 메서드가 될 수 없다 !!!!!!!
constructor 메서드는 인스턴스를 반환해야 하지만 async 함수는 언제나 프로미스를 반환해야 하기 때문이다 !!!!!!!!

🍀 await 키워드

  • 프로미스가 settled 상태 (비동기 처리가 수행된 상태) 될때까지 대기하다가 settled 상태가 되면 프로미스가 resolve한 처리 결과를 반환한다.
  • await 키워드는 반드시 프로미스 앞에서 사용해야 한다.
async function foo() {
  const a = await new Promise((resolve) => setTimeout(() => resolve(1), 3000));
  const b = await new Promise((resolve) => setTimeout(() => resolve(2), 2000));
  const c = await new Promise((resolve) => setTimeout(() => resolve(3), 1000));

  console.log([a, b, c]); // [1, 2, 3]
}
foo();

총 6s가 소요된다.
그런데 foo함수가 수행하는 3개의 비동기 처리는 서로 연관이 없이 개별적으로 수행되는 비동기 임으로 순차적으로 처리할 필요가 없다

async function foo() {
  const res = await Promise.all([
    new Promise((resolve) => setTimeout(() => resolve(1), 3000)),
    new Promise((resolve) => setTimeout(() => resolve(2), 2000)),
    new Promise((resolve) => setTimeout(() => resolve(3), 1000)),
  ]);

  console.log(res);
}

foo();

다음 bar 함수는 앞에 비동기 처리의 결과를 가지고 비동기 수행을 처리해야한다.

async function bar(n) {
  const a = await new Promise((resolve) => setTimeout(() => resolve(n), 3000));
  const b = await new Promise((resolve) =>
    setTimeout(() => resolve(a + 1), 2000)
  );
  const c = await new Promise((resolve) =>
    setTimeout(() => resolve(b + 1), 1000)
  );

  console.log([a, b, c]);
}

bar(1);

처리 순서가 보장되어야 하므로 모든 프로미스에 await 키워드를 써서 순차적으로 처리 해야한다.

🍀 에러처리

async/await에서는 에러 처리를 try...catch를 통해 할수 있다.

const fetch = require("node-fetch");

const foo = async () => {
  try {
    const wrongUrl = "https://wrong.url"; // 잘못된 url

    const response = await fetch(wrongUrl);
    const data = await response.json();
    console.log(data);
  } catch (err) {
    console.log(err);
  }
};

foo();

catch 문은 http 통신에서 발생한 네트워크 에러뿐 아니라 try 코드 블록 내의 모든 문에서 발생한 일반적인 에러까지 모두 캐치할 수 있다.

그런데 여기서 의문이였던 점은 프로미스를 사용하면 try, catch를 쓰지 않아도 then / catch / finally로 할수 있지 않나 ? 했는데

const fetch = require("node-fetch");

const foo = async () => {
 const wrongUrl = "https://wrong.url"; // 잘못된 url

 const response = await fetch(wrongUrl);
 const data = await response.json();
 return data;
};

foo()
	.then(console.log())
   .catch(console.error);

async 함수 내에서 catch 문을 사용해서 에러 처리를 하지 않으면, async 함수는 발생한 에러를 reject 하는 프로미스를 반환한다고 한다.

결과는 try, catch 써라 인거 같다


출처
https://ko.javascript.info/generators
모던 자바스크립트 책

profile
🍀

0개의 댓글