call back , promise , async await

fe_sw·2022년 7월 31일
0

Javascript

목록 보기
8/22
post-thumbnail

callback, promise, async/await는 (싱글스레드로 이루어져) 단일 호출 스택을 가진 자바스크립트에서 비동기를 처리할 수 있게 하는 방법들입니다.

call back

자바스크립트에서 콜백 함수는 다른 함수의 매개변수로 함수를 전달하고, 어떠한 이벤트가 발생한 후 매개변수로 전달한 함수가 다시 호출되는 함수이다.

함수의 처리 순서를 보장하는 과정에서 함수를 중첩해서 사용하게 되고, 이른바 콜백지옥이라는 상황이 발생해, 가독성과 유지보수가 떨어지게 된다.


const wait = (inputStr, callback) => {
  setTimeout(() => {
    console.log(inputStr);
    callback();
  }, 500);
};

wait('A', () => {
  wait('B', () => {
    wait('C', () => {
      return;
    });
  });
});

promise

call back은 비동기 처리 중 발생한 에러의 처리가 곤란하며 여러 개의 비동기 처리를 한 번에 처리하는데 한계를 보이는데 이때 도입된게 프로미스이다.

프로미스란 생성된 시점에는 결과를 아직 반환하지 않은 객체를 뜻한다.

promise 상태

new Promise()로 프로미스를 생성하고 종료될 때까지 3가지 상태를 갖는다.

  • Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
  • Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
  • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태

그리고 이행,실패상태가 되면 then,catch 후속 메서드로 처리 결과 값을 받을 수 있다.

  • then : 비동기 처리가 성공하여 지정된 콜백함수가 실행되고, 처리 결과 값을 받을 수 있습니다
  • catch : 비동기 처리가 실패하여 지정된 콜백함수가 실행되고, 실패 처리의 결과 값을 받는다
  • finally: 비동기 처리 성공여부를 떠나 지정된 콜백함수가 실행된다.

Pending(대기)


new Promise(function(resolve, reject) {
  // ...
});

//new Promise() 메서드를 호출하면 대기(Pending) 상태가 됩니다.
//new Promise() 메서드를 호출할 때 콜백 함수를 선언할 수 있고, 콜백 함수의 인자는 resolve, reject이다
//resolve는 성공할때 호출하고, reject는 실패할때 호출한다

Fulfilled(이행)


function getData() {
  return new Promise(function(resolve, reject) {
    var data = 100;
    resolve(data);
  });
}


getData().then(function(resolvedData) {
  console.log(resolvedData); // 100
});

//처리에 성공했다면 resolve를 호출해 프로미스를 fulfilled 상태로 변경된다

Rejected(실패)

function getData() {
  return new Promise(function(resolve, reject) {
    reject(new Error("Request is failed"));
  });
}


getData().then().catch(function(err) {
  console.log(err); // Error: Request is failed
});
//처리에 실패했다면 resolve를 호출해 프로미스를 rejected 상태로 변경된다

promise 오해

동기적 처리를 비동기적 처리로 바꿔주는 문법이 아님 ,단지 비동기 callback을 promise로 감싸서 쓰는것

promise 메서드

Promise.all

모든 프라미스가 이행될 때까지 기다리다가, 그 결과값을 담은 배열을 반환하는 매서드
(하나라도 reject가 호출된다면, Promise.all 역시 reject를 호출하고 가장 먼저 reject된 프로미스의 거부사유(reason)를 사용한다)

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value); // expected output: "two"
  // Both resolve, but promise2 is faster
});
출처: https://mine-it-record.tistory.com/622 [나만의 기록들:티스토리]

Promise.race

모든 결과가 완료되기까지 기다리지 않고, 가장 먼저 성공적으로 이행되거나 실패한 프로미스를 반환한다

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value); // expected output: "two"
  // Both resolve, but promise2 is faster
});

Promise.allSettled

주어진 모든 프로미스를 이행하거나 거부한 후, 각 프로미스에 대한 결과를 나타내는 객체 배열을 반환한다

Promise.allSettled([
  Promise.resolve(33),
  new Promise(resolve => setTimeout(() => resolve(66), 0)),
  99,
  Promise.reject(new Error('an error'))
])
.then(values => console.log(values));

// [
//   {status: "fulfilled", value: 33},
//   {status: "fulfilled", value: 66},
//   {status: "fulfilled", value: 99},
//   {status: "rejected",  reason: Error: an error}
// ]
출처: https://mine-it-record.tistory.com/622 [나만의 기록들:티스토리]

async await

간단하고 가독성 좋게 비동기 처리를 동기 처리처럼 동작하도록 구현할 수 있는 async/await가 도입되었다.

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

await 키워드는 async 키워드가 붙어있는 함수 내부에서만 사용할 수 있고, 비동기 함수가 return하는 promise로부터 결과값을 추출해준다.

즉, await 키워드를 사용하면 일반 비동기 처리처럼 바로 실행이 다음 라인으로 넘어가는 것이 아니라, 결과값을 얻을 수 있을 때까지 기다려주기 때문에 일반적인 동기 코드처리와 동일한 흐름으로 코드를 작성할 수 있다.


async function cal() {
  try { //try 를 먼저 실행해보고 실행되면 try 문 그대로 출력
    const result = await new Promise((resolve, reject) => {
   	const sum = 1 + 1;
    resolve(sum);
  });
    console.log(result)
  } catch { // try 가 실패시, catch 문 출력
    console.log('fail')
  }
}

cal();

async

함수에 async만 붙이면 자동 Promise객체로 인식되고, return값은 resolve()값과 똑같다.


async function p2(){ // async을 지정해주면 Promise를 리턴하는 함수로 만들어준다.
  return 'hello2'; 
  //resolve: Promise{<resolved>: "hello2"}
  //reject: throw 에러를 날리면 된다.
}
 
p2().then((n) => console.log(n)); //hello2

await

await 키워드는 프로미스가 settled 상태(비동기 처리가 수행된 상태) 가 될 때까지 대기하다가
Fulfilled 상태가 되면 프로미스가 resolve한 처리 결과를 반환한다.

주의점

async await로 구현할 경우 제너레이터의 성질을 갖기 때문에 항상 프로미스가 fulfilled(이행된) 상태가 될 때까지 대기한다.

따라서 모든 코드에 async await를 남발하는 것은 도움이 되지 않는다

async 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]
}44
foo(); // 약 6초 소요된다.

프로미스의 정적 메서드 Promise.all을 사용하는 경우

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); // [1, 2, 3]
}
foo(); // 약 3초 소요된다.

promise와 async/await 차이점

  • 에러 핸들링
    Promise 를 활용할 시에는 .catch() 문을 통해 에러 핸들링을 해야 하지만,
    async/await 은 try / catch를 통해 에러를 처리할 수 있다.

  • 코드 가독성
    Promise의 후속 처리 메서드인 .then() 의 hell의 가능성
    async/await 은 프로미스의 후속 처리 메서드 없이 마치 동기 처리처럼
    프로미스가 처리 결과를 반환하도록 구현할 수 있기 때문에 코드 흐름을 이해 하기 쉽다.


참고

0개의 댓글