콜백 지옥을 막아라!! Promise!!

조 은길·2022년 6월 12일
0

Javascript 정리

목록 보기
28/48
post-thumbnail

들어가기

지난 시간에 "콜백 함수"라는 디자인 패턴에 대해서 알아봤다.

그러나, 콜백은 길어질 경우 코드가 우측으로 넓어진다는 단점이 존재한다.
=> 콜백 지옥!!

이렇게 우측으로 길어지는 콜백함수 대신 코드가 길어지지 않는 새로운 디자인 패턴이 등장했으니 바로 Promise 디자인패턴이다.

이건 자바스크립트의 새로운 기능이라기보다는 코드/함수 디자인 패턴일 뿐입니다.

이런거 안써도 코드잘짤 수 있긴 한데, 나오면 알아야하니 아무튼

그럼 일단 어떻게 하는지 알아봅시다.


Promise 구조


let 프로미스 = new Promise(); // 성공 or 실패를 판정해주는 기계

프로미스.then(function(){
	// 성공시, 실행되는 코드
}).catch(function(){
	// 실패시, 실행되는 코드 
}).finally(function(){
	// 성공이든, 실패든 뭔가 일어났을 때 실행
});

이것이 끝입니다.

new Promise() 문법으로 프로미스라는 변수 오브젝트를 하나 생성하시면 Promise 제작 끝이다.

그럼 이제 프로미스라는 변수에다가 then()을 붙여서 실행가능하다.

프로미스 안의 코드가 실행이 완료가 되었을 때 then() 함수 내의 코드를 실행시켜준다.

코드가 실행이 실패했을 경우엔 catch() 함수 내의 코드를 실행시켜준다.

마지막으로 옵션으로 finally()라는 함수도 붙일 수 있다.
이 경우는 코드가 성공하든, 실패하든 둘 중 하나가 실행된 다음에 함수 내의 코드를 실행해준다.

(지금은 프로미스 안에 코드가 암것도 없지만요)

이런 식으로 코드를 차례로 실행할 수 있게 도와주는 디자인 패턴이 바로 Promise이다.

이거는 혁명이다.

콜백 함수는 뭐가 실행됐을 때, "이거를 실행해주세요~" 밖에 못했는데, Promise 패턴이 등장하면서, 실패했을 경우, 실행될 코드 역시 만들어낼 수 있게 된거다.


Promise가 콜백함수보다 좋다고 하는 이유 2가지

  1. 콜백함수와는 다르게 순차적으로 뭔가를 실행할 때 코드가 옆으로 길어지지 않는다.
    then 함수를 붙여서 순차적으로 실행하니까.

  2. 콜백함수는 불가능한 '실패시 특정 코드를 실행해주세요~' 라고 코드를 짤 수 있다. (catch)


Promise의 정확한 뜻과 사용법

우선 Promise를 사용해주기 위해서는

new Promise()안에는 반드시 콜백함수를 하나 넣어줘야 하는데, 그 콜백 함수는 2개의 파라미터를 받는다. 보통 관습적으로 resolve, reject라고 쓰는데, 각각 성공과 실패를 나타낸다.

let 프로미스 = new Promise(function(resolve, reject){
  
});

그리고 아래와 같이 어떤 경우 "성공"이고, "실패"인지를 구분해줘야 한다.

let 프로미스 = new Promise(function(성공, 실패){
  // 여기서, 특정 연산을 하든 뭘 하든해서
  // 무엇이 성공이고, 실패인지를 설정해줘야 한다.
  성공();
  // 이 경우 then 구문이 실행된다.
  실패();
  // 이 경우 catch 구문이 실행된다.
});

" 성공하면 then(), 실패하면 catch()를 실행해주세요~ " 라는 코드를 짤 수 있게 도와주는게 바로 Promise이다.

그럼 Promise는 성공과 실패 상황을 알려줘야겠죠?

그래서 Promise를 를 쉽게 정의하자면 성공&실패 판정기계이다.

Promise 기계 안에는 아무거나 다 집어넣을 수 있습니다

1 + 1같은 어려운 연산이 끝나면 성공판정 내려주세요~

페이지 내의 <button>을 누르면 성공으로 판정해주세요 ~

Ajax 요청으로 서버의 데이터를 가져오면 성공판정, 에러나면 실패판정해주세요~

아무거나 다 집어넣을 수 있습니다.

Promise 안에서 성공/실패를 판정하는 방법은 쉽습니다. 그대로 따라쓰시면 됩니다.


var 프로미스 = new Promise(function(성공, 실패){
  성공();
});

프로미스.then(function(){

}).catch(function(){

});

Promise()안에 콜백함수를 하나 추가해주시면 그 안에서 성공/실패 판정을 내릴 수 있습니다.

성공()이라고 첫째 파라미터를 함수형태로 작성하면 성공판정이 됩니다.

실패()라고 둘째 파라미터를 함수형태로 작성하면 실패판정이 됩니다.

위의 코드는 무조건 성공()을 실행하게 되어있으니 무조건 성공을 판정내리며

그 후엔 이제 then()안의 코드가 실행이 되겠죠?


실제로 사용하는 예시를 봅시다.

ex1) 힘든 수학연산 성공 후에 특정 코드를 실행하려면?

콜백함수로 디자인해놓아도 될 것 같죠? 그럼 그렇게 해도 된다.

근데 저는 콜백보다는 .then()을 쓰고싶어서 Promise를 한번 활용해보겠습니다.


let 프로미스 = new Promise(function(성공, 실패){
  var 어려운연산 = 1 + 1;
  성공();
});

프로미스.then(function(){
  console.log('연산이 성공했습니다')
}).catch(function(){

});

Promise()안에 어려운 수학 연산을 해주는 기능을 추가했다.

그리고 그 연산이 완료되면 성공() 코드를 실행하도록 코드를 추가했다.

(일반 코드들은 저렇게 위아래로 나란히 적으면 그냥 차례로 실행됩니다)

기계를 잘 만들어놨으니 then 함수 안에는 프로미스가 성공판정을 내리면, 실행할 코드를 담을 수 있다.

그럼 디자인 끝!!

  1. 이제 프로미스 내의 1+1 이라는 어려운 수학연산이 완료되면 성공() 판정을 내리며,

  2. 성공시 then() 내의 코드를 실행해줍니다.

이렇게 Promise를 사용하시면 이상한 콜백함수패턴 대신 멋있는 then을 사용할 수 있군요.


Promise 내에선 특정상황시 실패판정을 내릴 수도 있다

이렇게 하시면 됩니다.


var 프로미스 = new Promise(function(성공, 실패){
  var 어려운연산 = 1 + 1;
  실패();
});

프로미스.then(function(){
  console.log('연산이 성공했습니다')
}).catch(function(){
  console.log('실패했습니다')
});

실패()라는 함수를 실행하는 순간 실패판정을 내립니다.

그렇게 되면 catch() 내의 코드를 실행해줍니다.

실패의 경우 다른 내용을 실행해줄 수도 있고하니 그냥 콜백함수 디자인보다 훨씬 뭔가 직관적이고 유용합니다.

then과 catch 안의 콜백함수에 parameter를 추가할 수있다

참고로 연산결과같은걸 then 안에서 활용하고 싶으면

성공(); 함수 구멍안에 넣어주시면 됩니다.


let 프로미스 = new Promise(function(성공, 실패){
  let 어려운연산 = 1 + 1;
  성공(어려운연산);
});

프로미스.then(function(결과){
  console.log('연산이 성공했습니다' + 결과)
}).catch(function(){
  console.log('실패했습니다')
});

그럼 then 함수 안에서 파라미터의 형태로 그 결과를 사용하실 수 있다.


실제로 사용하는 예시를 하나 더 봅시다.

ex2) 1초 대기 성공 후에 특정 코드를 실행하려면?

역시 콜백함수로 디자인해놓아도 될 것 같죠? 그럼 그렇게 하시면 됩니다.

하지만 저는 간지나게 then을 사용해보기 위해 Promise를 디자인해보겠습니다.

let 프로미스 = new Promise(function(성공, 실패){
  setTimeout(function(){
    성공();
  }, 1000);
});

프로미스.then(function(){
  console.log('1초 대기 성공했습니다')
}).catch(function(){
  console.log('실패했습니다')
});

이러면 되겠죠?

아마 실패하는 경우는 없을 것 같아서 실패()는 안썼습니다.


Promise를 어디다 쓰는가?

Ajax, setTimeout, AddEventListener 같은 것들 안에 콜백함수를 쓰기 싫은 경우, 콜백 함수 대신에 조금 더 코드를 예쁘게 디자인할 수있다.

- ex) 1초 후에 성공하는 Promise 그리고 성공시 특정 코드를 실행하는 코드를 만들어보자!

let 프로미스 = new Promise( function(성공, 실패){
  		setTimeout( function() {
          	성공();
        }, 1000);
});
  1. 프로미스 기계 발동

  2. 성공 or 실패에 따라 코드 실행

  • 1초 전체 Devtool에 프로미스 콘솔을 찍어보면, pending을 담은 Promise 객체를 뱉어낸다.
  • 1초 후에 찍어보면, resolved를 담은 Promise 객체를 뱉어낸다.

Promise의 몇가지 특징

Promise의 3가지 상태

  • 일단 new Promise()로 생성된 변수를 콘솔창에 출력해보시면 현재 상태를 알 수 있다.

성공/실패 판정 전에는 프로미스를 출력해보면, <pending> 이라고 나온다.

=> 맨 앞에 Promise는 부모를 뜻하는 키워드

성공 후엔 <resolved> or <fulfilled>

가 출력되고, then() 구문이 실행됐음을 알 수있다.

실패 후엔 <rejected>

가 출력되며, catch() 구문이 실행됐음을 알 수 있다.

즉, 이렇게 상태를 나타내는 키워드를 통해 어떤 구문이 실행됐는지를 알 수있다.

성공을 실패나 대기상태로 다시 되돌릴 순 없습니다. 참고로, 알아둡시다.

Promise에 대한 오해

  • Promise는 동기를 비동기로 만들어주는 코드가 아니다.

Promise는 비동기적 실행과 전혀 상관이 없다.

그냥 코딩을 예쁘게 할 수 있는 일종의 디자인 패턴이다.

  • 예를 들면..

Promise 안에 10초 걸리는 어려운 연산을 시키면 10초동안 브라우저가 멈춘다.

10초 걸리는 연산을 해결될 때 까지 대기실(Web API)에 제껴두고 그런거는 아니다.

즉, 10초동안 결과가 판정이 될 떄까지 then(), catch() 어떤 것도 대기만 타고 있다.

원래 자바스크립트는 평상시엔 동기적으로 실행이 되며 비동기 실행을 지원하는 특수한 함수들 덕분에 가끔 비동기적 실행이 될 뿐이다.


Promise가 적용된 곳들

  • jQuery.ajax()
    $.ajax().done(function(){}).fail(...) => 이거 Promise랑 유사한 패턴 아닌가요??


  • fetch()
    얘는 항상 Promise를 return한다.
    즉, 성공/실패를 판정해서 fetch().then().catch()를 실행할 수있다.
    => 날씨 API Sprint에서 해봤음!!

profile
좋은 길로만 가는 "조은길"입니다😁

0개의 댓글