콜백지옥해결 Promise, async / await

5o_hyun·2022년 2월 25일
3
post-thumbnail

비동기 처리방법

자바스크립트의 비동기 처리방법은 3가지가 있다.

  • Callback : 함수안에 함수를 넣어 순서를 정한다. 단 함수가 많을수록 작성하기도 어렵고 가독성이 떨어진다 --> 콜백지옥
  • Promise : .then으로 함수실행순서를 정할 수 있다. 위의 callback 처럼 함수들이 많아질수록 작성하기가 어렵고 가독성이 떨어진다
  • async, await : 꼭 async 가 있어야 await 을 쓸 수 있고, 위 두가지 방법보다 작성하기 쉽고 가독성도 올라간다. 다만 아직 완전히 해결되는건 아니므로 callback과 promise를 적절히 섞어쓰면 된다.

Promise

promise는 언젠가 완료가 되는 작업의 결과값을 담는 상자와 같은 역할 을 한다.
콜백지옥을 해결하기 위한 Promise는 다음 중 하나의 상태를 가진다.

  • 대기 ( pending ) : 이행하지도 거부하지도 않은 초기 상태
  • 이행 ( fulfill ) : 연산 성공
  • 거부 ( reject ) : 연산 실패

Promise 기반 비동기적 함수를 호출하면 그 함수는 Promise 인스턴스를 반환한다.
대기중인 promise는 값과 함께 성공(fulfilled) 할 수도, 실패(rejected) 할 수도 있다.
이행되거나 거부될때 프로미스의 then 메서드에 의해 대기열 (큐) 에 추가된 처리기들이 호출된다.

기본예제

let firstPromise = new Promise((resolve, reject) => {
  // 비동기작업이 성공인경우 resolve(...), 실패인경우 reject(...)를 호출
  setTimeout(() => {
    resolve('성공~');
  }, 1000);
});

firstPromise.then((successMessage) => {
  // successMessage는 위에서 resolve(...) 호출에 제공한 값
  console.log('우와!' + successMessage);
});

// 우와!성공~

Promise의 생성

Promise는new Promise 생성자를 통해서 Promise 객체를 만들 수 있다.
Promise 생성자 함수는 비동기 처리를 수행 할 콜백 함수를 인수로 전달 받는데, 이 콜백 함수는 resolvereject함수를 인수로 전달받는다.

const promise = new Promise((resolve,reject) => {
  // Promise 함수의 콜백함수내부에서 비동기 처리를 수행
  if(/*비동기처리성공*/ ){
    resolve('result');
  } else {/*비동기처리실패*/
    reject('fail result');
  }
});

Promise 생성자 함수가 인수로 전달받은 콜백함수내부에서 비동기처리를 수행한다.
이 때 비동기처리가 성공하면 콜백함수의 인수로 전달받은 resolve함수를 호출하고, 실패하면 reject함수를 호출한다.

기본적으로 pending상태에서,

  • 비동기 처리 성공 : resolve함수를 호출해 promise를 fulfilled상태로 변경
  • 비동기 처리 실패 : reject함수를 호출해 promise를 rejected상태로 변경

pending상태에서 fulfilled 혹은 reject상태로 변경되면 setteled상태가 된다.
이는 pending상태가아닌 비동기처리가 수행된 상태를 말한다.

Promise의 후속처리 메서드

프로미스의 비동기 처리 상태가 변화하면 이에 따른 후속 처리를 해야한다.
예를들어, 프로미스가 fulfilled상태가 되면 프로미스의 처리결과를 가지고 무언가를 해야하고, rejected상태가되면 프로미스의 처리결과(에러)를 가지고 에러처리를 해야한다.
이를 위해 프로미스는 후속처리메서드 then, catch, finally를 제공한다.
즉, 프로미스의 비동기 처리상태가 변화하면 후속처리메서드에 인수로 전달한 콜백함수가 선택적으로 호출된다.

Promise.prototype.then

Promise.prototype.then
then 메서드는 두개의 콜백함수를 인수로 전달받는다.
첫번째 콜백함수는 비동기처리가 성공했을 때 호출되는 성공 처리 콜백함수고, 두번째 콜백함수는 비동기처리가 실패했을 때 호출되는 실패 처리 콜백함수다.
then 메서드는 언제나 프로미스를 반환한다.

Promise.prototype.catch

Promise.prototype.catch
catch 메서드는 한개의 콜백함수를 인수로 전달받는다.
catch 메서드의 콜백함수는 프로미스가 rejected상태인 경우만 호출된다. ( 에러처리를 해줄 때 사용한다. )
또한 then 메서드와 동일하게 동작하기때문에, 언제나 프로미스를 반환한다.

const wrongUrl = 'http://jsonplaceholder.typicode.com/XXX/1'

// 부적절한 URL이므로 에러발생
PromiseGet(wrongUrl)
	.then(res => console.log(res))
	.catch(err => console.error(err)); // Error : 404

Promise.prototype.finally

Promise.prototype.finally
finally 메서드는 한개의 콜백함수를 인수로 전달받는다.
프로미스의 상태와 상관없이 무조건 한 번 호출된다.
즉, finally메서드는 프로미스 상태와 상관없이 공통적으로 수행해야 할 처리내용이 있을 때 유용하다.

Promise의 정적 메서드

Promise는 주로 생성자 함수로 사용되지만 함수도 객체이므로 메서드를 가질 수 있다.

Promise.resolve Promise.reject : 이미 존재하는 값을 프로미스로 생성하기 위해 사용

// 배열을 resolve하는 프로미스 생성
const promise = Promise.resolve([1,2,3]);
promise.then(console.log); // [1,2,3]

// 에러객체를 reject하는 프로미스 생성
const promise = Promise.reject(new Error('error'));
promise.catch(console.log); // Error: error

Promise.all

Promise.all
여러개의 비동기 처리를 병렬처리를 하여 시간을 단축시킨다.

const result1 = () =>
  new Promise(resolve => setTimeout(() => resolve(1),3000));
const result2 = () =>
  new Promise(resolve => setTimeout(() => resolve(2),2000));
const result3 = () =>
  new Promise(resolve => setTimeout(() => resolve(3),1000));


Promise.all([result1,result2,result3])
  .then(console.log) // [1,2,3] => 총 6초걸릴거 3초 소요
  .catch(console.error);

Promise.all은 배열 등의 이터러블을 인수로 전달받는다.
전달받은 모든 프로미스가 fulfilled 상태가 되면 모든 처리결과를 배열에 저장해 새로운 프로미스를 반환한다.

Promise.race

Promise.race
제일 처음 완료되는 promise객체를 반환한다.

Promise.race([
  new Promise(resolve => setTimeout(() => resolve(1),3000)), // 1
  new Promise(resolve => setTimeout(() => resolve(2),2000)), // 2
  new Promise(resolve => setTimeout(() => resolve(3),1000)) // 3
])
  .then(console.log) // 3
  .catch(console.error);

fetch

fetch함수는 HTTP 요청 전송 기능을 제공하는 클라이언트 사이드 Web API이다.
fetch함수는 HTTP응답을 나타내는 Response 객체를 래핑한 Promise 객체를 반환한다.

fetch('https://jsonplaceholder.typicode.com/todos')
  .then(res => console.log(res));

async, await

ES8에서 도입된 async , await를 사용하면 비동기함수를 마치 동기적 코드인거처럼 동작하도록 구현할 수 있다.
즉, 프로미스의 후속처리 메서드(then, catch, finally) 없이 마치 동기 처리처럼 프로미스가 처리결과를 반환하도록 구현할 수 있다.

비동기함수는 항상 promise객체를 반환한다는 특징이 있다.
async함수는 async키워드를 사용해 언제나 프로미스를 반환한다.
async함수가 명시적으로 프로미스를 반환하지 않더도 async함수는 암묵적으로 반환값을 resolve하는 프로미스를 반환한다.

async는 asynchronous의 줄임말로 비동기를 의미한다. 이 함수 안에 비동기적으로 실행될 부분(await)이 있다는 것을 의미한다.

async function test() {
    // logic
}

변수 = async() => {
	// logic
}

await는 async 안에서만 작동한다. await 키워드를쓰게되면 해당값이 반환 되기 전까지 기다리는 동안 async 내부 함수는 일시 중단이 된다.
( 프로미스가 settled상태 ( 비동기처리가 수행된 상태 )가 될때까지 대기하다가 settled상태가 되면 프로미스가 resolve한 처리결과 값을 반환한다.)

async, await 이용 http 통신방법

function userInfo() {
  const url = 'https://koreanjson.com/users/1';
  return fetch(url)
    .then((res) =>{
    return res.json();
  })
}

function userTodo() {
  const url = 'https://koreanjson.com/todos/1';
  return fetch(url)
    .then((res) => {
    return res.json(); 
  })
}

async function sumOfFetch() {
  const user = await userInfo();
  if(user.id === 1) {
   const todo = await userTodo();
   console.log(todo)
   return todo;
  }
}
sumOfFetch();

// {id: 1, title: "모든 국민은 직업선택의 자유를 가진다.", completed: true, createdAt: "2019-02-24T16:17:47.000Z", updatedAt: "2019-02-24T16:17:47.000Z"…}

async, await 예외처리 try catch 문

error 처리를 할 경우에는 정상적으로 동작 되었을 때 실행되는 부분을 try 문으로 감싸고 err가 발생 시 실행되는 부분을 catch문으로 감싼다.

async function sumOfFetch() {
  try{
    const user = await userInfo();
    if(user.id === 1) {
    const todo = await userTodo();
    console.log(todo)
    return todo;
    }
  } catch (e) {
    console.error(e);
  }
}
sumOfFetch();
profile
학생 점심 좀 차려

0개의 댓글