Asynchronous Code2(Async and Await)

codeFug·2024년 1월 5일

JS

목록 보기
7/8
post-thumbnail

Promise가 있는데 이제 끝난거 아니야?

Promise에서 .then .catch가 왜 필요한지는 알꺼다. 하지만 다음을 보자.

  function addPromise(x){
    return new Promise(resolve => {
      doubleAfter2Seconds(10).then((a) => {
        doubleAfter2Seconds(20).then((b) => {
          doubleAfter2Seconds(30).then((c) => {
            resolve(x + a + b + c);
          })
        })
      })
    });
  }
  ```

Promise를 사용했을지라도, x+a+b+c 하는 코드일 뿐인데도, 또 하나의 콜백지옥에 들어가게 된다.

이를 해결하는 방법으로는 async, await이라는 keyword가 있다.

function doubleAfter2Seconds(x) {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve(x * 2);
        }, 2000);
      });
}
async function addAsync(x) {
      const a = await doubleAfter2Seconds(10);
      const b = await doubleAfter2Seconds(20);
      const c = await doubleAfter2Seconds(30);
      return x + a + b + c;
}
addAsync(10).then((sum) => {
console.log(sum);
});

asyncawait 은 동기적 코드를 비동기적 코드처럼 만들 수 있는 keyword들이다. 아래 예제 2개는 정확히 같다고 볼 수 있다.

  function getPersonsInfo(name) {
    return server.getPeople().then(people => {
      return people.find(person => { return person.name === name });
    });
  }
  async function getPersonsInfo(name) {
    const people = await server.getPeople();
    const person = people.find(person => { return person.name === name });
    return person;
  }

위의 예제의 server는 다음과 같다.

    const server = {
      people: [
        {
          name: "Odin",
          age: 20,
        },
        {
          name: "Thor",
          age: 35,
        },
        {
          name: "Freyja",
          age: 29,
        },
      ],
      getPeople() {
        return new Promise((resolve, reject) => {
          // Simulating a delayed network call to the server
          setTimeout(() => {
            resolve(this.people);
          }, 2000);
        });
      },
    };

확인해보면 비동기적으로 서버의 데이터를 받은 후에 서버는 객체의 people의 name property를 확인해서 해당되는 조건에 맞으면 person을 뱉는 간단한 코드이다.

Promise를 간단하게 구현해놓은 것이라고 볼 수 있는데, 한번 확인해보자.

Async Keyword

async 는 JS engine에 비동기적 함수를 선언하고 있다고 알려주는 것이다.
awaitasync 함수 안에 사용해야 한다. async 가 선언되면 항상 자동으로 Promise를 return한다. (return > resolve , throwing error > reject)

  1. async/await을 사용하면 .then이 거의 필요하지 않고 .catch도 필요하지 않을 수 있지만
  2. 최상위 레벨 코드에서는 await을 사용할 수 없기 때문에(전역 스코프) 그때 관행처럼
    .then/catch를 추가해서 최종 결과나 처리되지 못한 에러를 다룬다.
    async function은 Promises 와 문법적으로 일치하다.
    async 는 function이 있는 모든 경우에 사용될 수 잇다.
    예시>:arrow function
    const yourAsyncFunction = async () => {
    // do something asynchronously and return a promise
    return result;
};
  anArray.forEach(async item => {
      // do something asynchronously for each item in 'anArray'
      // one could also use .map here to return an array of promises to use with 'Promise.all()'
});

  server.getPeople().then(async people => {
      people.forEach(person => {
      // do something asynchronously for each person
  });
});

thenable

await는 thenable 객체를 받는다. 이 객체는 프라미스가 아니지만 프라미스와 호환 가능한 객체라는 뜻이다. 밑의 예제에서는 class에서 then이라는 메서드가 존재해서 그걸 await가 thenable하다고 판단하여 비동기적으로 만든 것이다.

class Thenable {
     constructor(num) {
     this.num = num;
 }
     then(resolve, reject) {
     alert(resolve);
     // 1000밀리초 후에 이행됨(result는 this.num*2)
     setTimeout(() => resolve(this.num * 2), 1000); // (*)
     }
};        
async function f() {
    // 1초 후, 변수 result는 2가 됨
    let result = await new Thenable(1);
    alert(result);
}        
f();

메서드 앞에 async를 추가하면 async 클래스 메서드가 선언된다.

  class Waiter {
  async wait() {
      return await Promise.resolve(1);
  }
}        
  new Waiter()
  .wait()
  .then(alert); // 1

Await keyword

  1. 함수를 진행하기 전에 asynchronous action을 기다리라고 JS에게 요청한다.

    1. 끝나기까지 기다리는 기능
    2. .then() 대신에 await keyword를 이용해서 function으로부터 value를 받을 수 있다.
  2. Promise.all
    1. 여러 개의 프라미스가 처리되길 기다려야 하는 상황이면 Promise.all로 감싸고 여기에 await를 붙여서 사용할 수 있다.

            // 프라미스 처리 결과가 담긴 배열을 기다립니다.
            let results = await Promise.all([
              fetch(url1),
              fetch(url2),
              ...
            ]);
  3. Error Handling

    1. async도 .catch 를 이용해서 error handling을 할 수 있다.

      async function f() {
        let response = await fetch('http://유효하지-않은-주소');
      }
      
      // f()는 거부 상태의 프라미스가 됩니다.
      f().catch(alert); // TypeError: failed to fetch // (*)
    2. 프라미스가 거부되기 전에 약간의 시간이 지체되는데 이런 경우 await가 에러를 던지기 전에 지연이 발생된다.

    3. 이러한 여러 error들을 try/catch 로 잡을 수 있다.

      async function getPersonsInfo(name) {
        try {
          const people = await server.getPeople();
          const person = people.find(person => { return person.name === name });
          return person;
        } catch (error) {
          // Handle the error any way you'd like
        }
      }

요약

1. async 키워드를 추가하면 두가지 효과
    1. 함수는 프라미스를 반환
    2. 함수 안에서만 await을 사용할 수 있다.
2. await을 붙이면 프라미스 처리까지 대기
    1. 에러 발생 - 예외 발생
    2. 에러 미발생 - 프라미스 객체의 result값을 반환
profile
https://github.com/codefug

0개의 댓글