home강의 홈으로
Section 14. 비동기 프로그래밍
Lesson 2. 프로미스

콜백 지옥 callback hell

setTimeout(() => { console.log(1); setTimeout(() => { console.log(2); setTimeout(() => { console.log(3); setTimeout(() => { console.log(4); setTimeout(() => { console.log(5); }, 500); }, 500); }, 500); }, 500); }, 500);

💡 연속적으로 비동기 코드를 써야 하는 경우

  • 위와 같이 콜백 함수 안에 또 다른 콜백 함수를 넣어야 하는 상황 발생 - 콜백 지옥
  • 횟수가 많을수록 가독성도 낮아지고 직관성이 떨어짐
  • 실전에서는 더더욱 복잡하고 난해해짐

🏃🏃🏃 릴레이 예제

  • 철수, 영희, 돌준, 정아, 길돈이 치례로 이어달리기는 하는 코드
  • 각 골인시간 기록 - 이전 콜백함수의 결과가 다음 콜백함수로 넘겨져 축적됨
  • 한 주자라도 데드라인(밀리초)을 넘기면 실패 - 주자마다 다른 실패 메시지 출력
  • 완주 실패시 😢 완주 실패 - ${전체 소요시간} 출력
  • 실패든 성공이든 마지막에 - - 경기 종료 - - 출력

const DEADLINE = 1400; function relayRun (name, start, nextFunc, failMsg) { console.log(`👟 ${name} 출발`); const time = 1000 + Math.random() * 500; setTimeout(() => { if (time < DEADLINE) { console.log(`🚩 ${name} 도착 - ${(start + time)/1000}초`); nextFunc?.(start + time); } else { console.log(failMsg); console.log(`😢 완주 실패 - ${(start + time)/1000}초`); } if (time >= DEADLINE || !nextFunc) { console.log('- - 경기 종료 - -'); } }, time); } relayRun('철수', 0, start1 => { relayRun('영희', start1, start2 => { relayRun('돌준', start2, start3 => { relayRun('정아', start3, start4 => { relayRun('길돈', start4, null, '아아, 아깝습니다...'); }, '정아에게 무리였나보네요.'); }, '돌준이 분발해라.'); }, '영희가 완주하지 못했네요.'); }, '철수부터 광탈입니다. ㅠㅠ');



프로미스 promise

  • (보통 시간이 걸리는) 어떤 과정 이후 주어진 동작을 실행할 것이란 약속
  • 중첩된 비동기 코드를 직관적이고 연속적인 코드로 작성할 수 있도록 함
  • 👉 MDN 문서 보기


const borrow = 20; // 빌린 돈의 10%를 더해 값겠다는 약속 // reject는 지금 사용하지 않음 const payWith10perc = new Promise((resolve, reject) => { resolve(borrow * 1.1); }); payWith10perc .then(result => { console.log(result + '만원'); });

생성자 Promise - 👉 MDN 문서 보기

  • 새로운 약속을 하는 코드
  • 인자로 받는 콜백함수의 첫 번째 인자 resolve ( 이름은 관례 ) - 약속 이행 성공시, 반환할 값 넣어 실행

프로미스 인스턴스 ( 만들어진 약속 )then 메서드

  • resolve를 통해 ( 약속대로 ) 반환된 결과를 인자로 하는 콜백 함수를 넣음
  • ⭐ 또 다른 프로미스를 반환 - 체이닝 가능
  • 👉 MDN 문서 보기 - ( 추가 인자 )


const borrow = 20; const payWith10perc = new Promise((resolve, reject) => { // 💡 내부에서 비동기 코드 사용 setTimeout(() => { resolve(borrow * 1.1); }, 1000); // 1초 후 갚겠음 }); // ⚠️ 콘솔에서 분리해서 실행하면 안 됨! // 프로미스가 생성되는 순간부터 시간 경과 payWith10perc .then(result => { console.log(result + '만원'); });
  • 일반적으로 내부에 비동기 코드를 사용
  • 시간이 소모되는 비동기 과정 후 ~를 반환하겠다는 약속


const borrow = 20; const payWith10perc = new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() < 0.5) { // 💡 돈을 갚을 수 없게 되었을 때 reject('사업 망함'); // 보통 실패사유나 관련 설명을 넣음 } resolve(borrow * 1.1); }, 1000); // 1초 후 갚겠음 }); payWith10perc .then(result => { console.log(result + '만원'); } // 💡 두 번째 인자로 reject를 받는 콜백을 넣을 수 있지만 // 아래처럼 catch로 진행하는 것이 더 깔끔함 ) .catch(msg => { console.error(msg); }) .finally(() => { console.log('기한 종료'); });

생성자 Promise

  • 인자로 받는 콜백함수의 두 번째 인자 reject ( 이름은 관례 ) - 약속 이행 실패시, 반환할 값 넣어 실행
  • reject가 실행되면 resolve는 무시됨

프로미스 인스턴스의

  • catch 메서드 : reject를 통해 ( 실패로 인해 ) 반환된 결과를 인자로 하는 콜백 함수를 넣음
  • finally 메서드 : 성공하든 실패하든 실행할 콜백 함수 - 필요할 때만 사용
  • then과 더불어 메서드 체이닝으로 사용


// ⭐ then은 연속적으로 메서드 체이닝 가능 new Promise((resolve) => { resolve(2); }) .then(i => i * 4) .then(i => i - 3) .then(i => i ** 2) .then((i) => { console.log(i); });

💰 10% 이자, 채무자 파산가능성 10%, 5번 빌려주기

// 빌린 금액으로 약속을 하는 함수 function moneyLend (borrow) { return new Promise((resolve, reject) => { console.log(`채무 ${borrow}만원`); setTimeout(() => { if (Math.random() < 0.1) { reject('채무자 파산'); } resolve(borrow * 1.1); }, 1000); }); } moneyLend(20) .then(result => moneyLend(result)) .then(moneyLend) // 인자를 하나 받아서 그대로 쓰므로 .then(moneyLend) // 이렇게 줄여버릴 수 있음 .then(moneyLend) .then(result => { console.log(`💰 반납 ${result}만원`); }) .catch(msg => { console.error(msg); }) .finally(() => { console.log('- - 대금업 종료 - -'); });



🏃🏃🏃 릴레이 예제 프로미스로 구현

const DEADLINE = 1400; function getRelayPromise (name, start, failMsg) { console.log(`👟 ${name} 출발`); // 💡 랜덤 시간만큼 달리고 결과를 반환하겠다는 약속을 만들어 반환 return new Promise((resolve, reject) => { const time = 1000 + Math.random() * 500; setTimeout(() => { if (time < DEADLINE) { console.log(`🚩 ${name} 도착 - ${(start + time)/1000}초`); resolve(start + time); } else { console.log(failMsg); reject((start + time) / 1000); } }, time); }) } getRelayPromise('철수', 0, '철수부터 광탈입니다. ㅠㅠ') .then(result => { return getRelayPromise('영희', result, '영희가 완주하지 못했네요.'); }) .then(result => { return getRelayPromise('돌준', result, '돌준이 분발해라.'); }) .then(result => { return getRelayPromise('정아', result, '정아에게 무리였나보네요.'); }) .then(result => { return getRelayPromise('길돈', result, '아아, 아깝습니다...'); }) .catch(msg => { console.log(`😢 완주 실패 - ${msg}초`); }) .finally(() => { console.log('- - 경기 종료 - -'); });

🤔얄코에게 질문하기질문은 반.드.시 이리로 보내주세요! ( 강의사이트 질문기능 ✖ )

강의에서 이해가 안 되거나 실습상 문제가 있는 부분,
설명이 잘못되었거나 미흡한 부분을 메일로 알려주세요!

답변드린 뒤 필요할 경우 본 페이지에
관련 내용을 추가/수정하도록 하겠습니다.

이메일 주소
yalco@yalco.kr
메일 제목 (반드시 아래 제목을 붙여넣어주세요!)
[질문] 제대로 파는 자바스크립트 (유료 파트) 14-2

🛑질문 전 필독!!

  • 구글링을 먼저 해 주세요. 들어오는 질문의 절반 이상은 구글에 검색해 보면 1분 이내로 답을 찾을 수 있는 내용들입니다.
  • 오류 메시지가 있을 경우 이를 구글에 복붙해서 검색해보면 대부분 짧은 시간 내 해결방법을 찾을 수 있습니다.
  • 강의 페이지에 추가사항 등 놓친 부분이 없는지 확인해주세요. 자주 들어오는 질문은 페이지에 추가사항으로 업데이트됩니다.
  • "유료파트의 강의페이지는 어디 있나요?" - 각 영상의 시작부분 검은 화면마다 해당 챕터의 강의페이지 링크가 있습니다.
  • 질문을 보내주실 때는 문제가 어떻게 발생했고 어떤 상황인지 등을 구체적으로 적어주세요. 스크린샷을 첨부해주시면 더욱 좋습니다.
🌏 Why not change the world?