JS 비동기 처리방식 - Promise

yurim·2020년 5월 14일
0
post-thumbnail

자바스크립트의 비동기 처리방식의 문제점을 해결하기 위한 방법으로 Promise가 있다.

Promise 란?

the promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.

Promise 객체는 비동기 작업의 미래의 완료 또는 실패와 그 결과 값을 나타낸다.

Promise 활용하기

  • Promise 생성
// arrow function 쓴 경우
const myPromise = new Promise((resolve, reject) => {
  //구현
})

const myPromise = new Promise(function(resolve, reject) {
  //구현
})

new Promise로 생성된 인스턴스 객체는 '객체'이기 때문에 변수로 할당하거나 함수의 인자로 사용할 수 있다.

executor의 인수 resolvereject는 자바스크립트가 자체적으로 제공하는 콜백이다.

  • resolve(value) --> 일이 성공적으로 끝난 경우, 그 결과를 나타내는 value와 함께 호출
  • reject(error) --> 에러 발생 시 에러 객체를 나타내는 error와 함께 호출
function getData(callbackFunc) {
  $.get('url 주소/products/1', function(response) {
    callbackFunc(response);
  });
}

getData(function(tableData) {
  console.log(tableData);

callback 함수를 활용하여 비동기처리를 한 위의 코드에서 Promise를 적용한다면, 아래와 같이 표현할 수 있다.

function getData(callback) {
  // new Promise() 추가
  return new Promise(function(resolve, reject) {
  	$.get('url 주소/products/1', function(response) {
      // data를 받는데 성공하면 resolve() 호출
      resolve(response);
    });
  });
}
//getData()의 실행이 끝나면 호출되는 then()
getData().then(function(tableData) {
  //resolve()의 결과 값이 여기로 전달됨
  console.log(tableData); //$.get()의 response 값이 tableData에 전달됨
});

Promise의 상태(States)

Promise는 이들 중 한가지의 상태를 갖는다.

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

인스턴스 호출

thencatch를 사용한다.

  1. resolve 되는 값은 then 메소드의 인자로 넘어간다.
  2. reject 되는 값은 catch 메소드의 인자로 넘어가서 에러 핸들링을 할 수 있다.
function getData(callback) {
  // new Promise() 추가
  return new Promise(function(resolve, reject) {
  	$.get('url 주소/products/1', function(response) {
      reject("데이터를 받아오는 데에 실패했습니다.");
    }); // promise가 reject되었을때, error을 나타내줌
  });
}
//getData()의 실행이 끝나면 호출되는 then()
getData()
	.then()
	.catch(e => console.log("error: ", e)) 
});

여기서 중요한 점은 then메소드는 다시 Promise를 반환한다는 것이다.
다시 Promise 객체를 반환하게 되면 then,catch 메소드를 사용할 수 있게 되며, 이를 통해 연속적으로 then을 사용하여 Promise Chaining이 가능하게 된다.

Promise Chaining

콜백에서 나타났던 콜백 지옥처럼, Promise에서도 여러개의 비동기 작업을 나타내는 Promise Chaining이 나타난다.

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000); // (*)

}).then(function(result) { // (**)

  alert(result); // 1
  return result * 2;

}).then(function(result) { // (***)

  alert(result); // 2
  return result * 2;

}).then(function(result) {

  alert(result); // 4
  return result * 2;

});
  1. 1초 후 최초 promise 이행
  2. .then 핸들러가 호출
  3. 2에서 반환한 값은 다음 .then 핸들러에 저장
  4. 이와 같은 과정 반복

Fetch와 Promise 같이 사용하기

실제 코딩을 할 때는 Promise 객체를 직접 생성해서 리턴해주는 코드 보다는 어떤 라이브러리의 함수를 호출해서 리턴받은 Promise 객체를 사용하는 경우가 더 많다.

대표적으로 REST API를 호출할 때 사용되는 브라우저 내장 함수인 fetch()가 있다. fetch()함수는 API의 URL을 인자로 받고, 미래 시점에 얻게될 API 호출 결과를 Promise 객체로 리턴한다.

fetch("url주소/products/1")
	.then((response) => console.log("response:", response))
    .catch((error) => console.log("error: ", error))
fetch("/article/promise-chaining/user.json")
	.then(response => response.json())
	.then(user => fetch(`https://api.github.com/users/${user.name}`))
	.then(response => response.json())
	.then(githubUser => new Promise(function(resolve, reject) { //(*)
  let img = document.createElement('img');
  img.src = githubUser.avatar_url;
  img.className = "promise-avatar-example";
  document.body.append(img);
  
  setTimeout(() => {
    img.remove();
    resolve(githubUser); //(**)
  }, 3000); 
}));

.then(githubUser => alert(`Finished showing ${githubUser.name}`));

(*)로 표시한 곳의 .then 핸들러는 이제 setTimeout안의 resolve(githubUser)를 호출했을 때((**)) 만 처리상태가 되는 new Promise를 반환한다. 체인의 다음 .then은 이를 기다린다.

비동기 동작은 항상 프라미스를 반환하도록 하는 것이 좋다. 지금은 체인을 확장할 계획이 없더라도 이렇게 구현해 놓으면 나중에 체인 확장이 필요한 경우 손쉽게 체인을 확장할 수 있다.

코드를 재사용 가능한 함수 단위로 분리한다면 다음과 같다.

function loadJson(url) {
  return fetch(url)
  	.then(response => response.json());
}

function loadGithubUser(name) {
  return fetch(`https://api.github.com/users/${name}`)
  	.then(response => response.json());
}

function showAvatar(githubUser) {
  return new Promise(function(resolve, reject) {
    let img = document.createElement('img');
    img src = githubUser.avatar_url;
    img.className = "promise-avatar-example";
    document.body.append(img);
    
    setTimeout(() => {
      img.remove();
      resolve(githubUser);
    }, 3000);
  });
}

loadJson('/article/promise-chaining/user.json')
	.then(user => loadGithubUser(user.name))
	.then(showAvatar)
	.then(githubUser => alert(`Finished showing ${githubUser.name}`));
profile
닭발먹고 힘내서 복습하자👻

0개의 댓글