앞서 콜백 지옥이라는 콜백 함수를 중첩해서 사용하여 가독성과 유지 보수성이 떨어지는 문제점이 발생하였다.
이 문제를 해결하기 위 Promise를 사용하여 콜백 지옥을 해결하며, 비동기 처리도 간결하게 작성할 수 있다.
콜백 지옥을 해결하기 위한 방안으로 ES6에 도입된 기능이다.
우선 Promise를 사용하기 위해 Promise 객체를 생성해야 한다.
const promise = new Promise((resolve, reject) => { ... });
Promise는 new 키워드와 같이 생성자를 이용하여 생성한다.
이때 new Promise를 하는 순간 이곳에 할당된 비동기 작업은 바로 시작되게 된다.
이 생성자는 executor라는 실행함수를 인자로 받으며, executer 함수에는 resolve, reject라는 2개의 함수형 매개변수를 갖는다.
▶ executor 함수 내부
: 비동기 작업을 실행하는 코드가 작성
: 해당 작업이 성공하였을 경우, 성공한 결과 값을 리턴하는 resolve 함수가 호출
: 실패할 경우 에러 처리를 하는 reject 함수가 호출
▶ resolve
: 함수 안의 처리가 끝났을 때 호출해야 하는 콜백 함수
: 어떤 값도 인수로 넘길 수 있다.
: 다음 처리를 실행하는 함수에 전달된다.
▶ reject
: 함수 안의 처리가 실패했을 때 호출해야 하는 콜백 함수
: 주로 오류 메시지 문자열을 인수로 사용한다.
Promise가 끝나고 난 다음의 동작을 설정해주기 위해 then / catch 메서드를 사용한다.
▶ then
: 해당 Promise가 성공했을 때 동작을 지정
: 인자로 함수를 받는다.
▶ catch
: 해당 Promise가 실패했을 때 동작을 지정
위 함수들은 chain 형태로 연속해서 호출할 수 있다.
then / catch 메서드를 사용한 예시를 보면서 이해가 쉬울 것이다.
const promise = new Promise((resolve, reject) => { resolve(); }); promise .then(() => { console.log("then 출력"); }) .catch(() => { console.log("catch 출력"); });
위의 코드 실행 결과 resolve를 호출하였기 때문에 "then 출력"결과가 출력된다.
const promise = new Promise((resolve, reject) => { reject(); }); promise .then(() => { console.log("then 출력"); }) .catch(() => { console.log("catch 출력"); });
다음 코드 실행 결과 reject가 호출되었으므로 "catch 출력"결과가 나올 것이다.
▶ Pending(대기)
: new Promise 메서드를 호출하기만 한 상태
: 비동기 처리 로직이 아직 완료되지 않은 상테
▶ Fulfilled(이행)
: resolve가 호출된 상태
: 비동기 처리가 완료되어 promise 결과 값을 반환해준 상태
▶ Rejected(실패)
: reject가 호출된 상태
: 비동기 처리가 실패하거나 오류가 발생한 상태
function increase(number) { const promise = new Promise((resolve, reject) => { setTimeout(() => { const result = number + 10; if (result > 50) { // result가 50보다 클 때 에러 발생시키게 하기기 const e = new Error("Number is Too Big"); return reject(e); } resolve(result); }, 1000); }); return promise; } increase(0) .then((number) => { console.log(number); return increase(number); }) .then((number) => { console.log(number); return increase(number); }) .then((number) => { console.log(number); return increase(number); }) .then((number) => { console.log(number); return increase(number); }) .then((number) => { console.log(number); return increase(number); }) .catch((e) => { console.log(e); });
.then을 사용하면 그 다음 작업을 설정하기 때문에 콜백 지옥이 형성되지 않는다.
위의 코드를 보면 then, catch에서 다시 다른 then이나 catch를 붙였다.
이전 then의 return 값을 다음 then의 매개변수로 넘긴다.
Promise를 return한 경우 Promise가 수행된 후 다음 then이나 catch를 호출하게 된다.
Promise.all을 사용하면 Promise 여러 개를 한 번에 실행할 수 있다.
이전 예시는 모두 비동기 처리 여러 개를 직렬로 연결해 순차적으로 실행하였다면, Promise.all을 이용하면 비동기 처리 여러 개를 병렬로 처리 가능하다.
Promise.all(iterable);
이때 iterable은 배열과 같이 반복 가능한 객체를 말한다.
function buySomething(name, nowMoney) { return new Promise((resolve, reject) => { setTimeout(() => { const pay = 500; const remain = nowMoney - pay; if (remain >= 0) { console.log(`${name} : ${pay}원 지불`); resolve(remain); } else { reject(`${name} : 잔액부족: 현재 잔액${nowMoney}원`); } }, 2000); }); } Promise.all([ buySomething("A", 500), buySomething("B", 1000), buySomething("C", 1500) ]) .then(remain => { console.log(remain); }) .catch(error => { console.log(error); });
위의 코드를 실행한 결과 remain은 [0, 500, 1000]으로 남게 될 것이다.
Promise.race는 가장 먼저 종료한 Promise 객체 결과만 다음 작업으로 보낼 때 사용한다.
function buySomething(name, nowMoney) { return new Promise((resolve, reject) => { setTimeout(() => { const pay = 500; const remain = nowMoney - pay; if (remain >= 0) { console.log(`${name} : ${pay}원 지불`); resolve(remain); } else { reject(`${name} : 잔액부족: 현재 잔액${nowMoney}원`); } }, 2000); }); } Promise.race([ buySomething("A", 500), buySomething("B", 1000), buySomething("C", 1500) ]) .then(remain => { console.log(remain); }) .catch(error => { console.log(error); });
Promise.race로 실행하면 A가 실행되고 남은 결과 값인 0만 반환될 것이다.
즉 buySomething("A", 500)이 가장 먼저 실행되며, 가장 먼저 종료되기 때문에 해당 작업의 결과 값만 반환된다.
하지만 나머지 작업도 실행은 될 것이다.
[JavaScript] 비동기 처리 - Promise
앞서 콜백 지옥이라는 콜백 함수를 중첩해서 사용하여 가독성과 유지 보수성이 떨어지는 문제점이 발생하였다.
이 문제를 해결하기 위 Promise를 사용하여 콜백 지옥을 해결하며, 비동기 처리도 간결하게 작성할 수 있다.
Promise
콜백 지옥을 해결하기 위한 방안으로 ES6에 도입된 기능이다.
우선 Promise를 사용하기 위해 Promise 객체를 생성해야 한다.
Promise는 new 키워드와 같이 생성자를 이용하여 생성한다.
이때 new Promise를 하는 순간 이곳에 할당된 비동기 작업은 바로 시작되게 된다.
이 생성자는 executor라는 실행함수를 인자로 받으며, executer 함수에는 resolve, reject라는 2개의 함수형 매개변수를 갖는다.
▶ executor 함수 내부
: 비동기 작업을 실행하는 코드가 작성
: 해당 작업이 성공하였을 경우, 성공한 결과 값을 리턴하는 resolve 함수가 호출
: 실패할 경우 에러 처리를 하는 reject 함수가 호출
▶ resolve
: 함수 안의 처리가 끝났을 때 호출해야 하는 콜백 함수
: 어떤 값도 인수로 넘길 수 있다.
: 다음 처리를 실행하는 함수에 전달된다.
▶ reject
: 함수 안의 처리가 실패했을 때 호출해야 하는 콜백 함수
: 어떤 값도 인수로 넘길 수 있다.
: 주로 오류 메시지 문자열을 인수로 사용한다.
then / catch 메서드
Promise가 끝나고 난 다음의 동작을 설정해주기 위해 then / catch 메서드를 사용한다.
▶ then
: 해당 Promise가 성공했을 때 동작을 지정
: 인자로 함수를 받는다.
▶ catch
: 해당 Promise가 실패했을 때 동작을 지정
: 인자로 함수를 받는다.
위 함수들은 chain 형태로 연속해서 호출할 수 있다.
then / catch 메서드를 사용한 예시를 보면서 이해가 쉬울 것이다.
위의 코드 실행 결과 resolve를 호출하였기 때문에 "then 출력"결과가 출력된다.
다음 코드 실행 결과 reject가 호출되었으므로 "catch 출력"결과가 나올 것이다.
Promise의 3가지 상태
▶ Pending(대기)
: new Promise 메서드를 호출하기만 한 상태
: 비동기 처리 로직이 아직 완료되지 않은 상테
▶ Fulfilled(이행)
: resolve가 호출된 상태
: 비동기 처리가 완료되어 promise 결과 값을 반환해준 상태
▶ Rejected(실패)
: reject가 호출된 상태
: 비동기 처리가 실패하거나 오류가 발생한 상태
Promise로 비동기 처리 연결
.then을 사용하면 그 다음 작업을 설정하기 때문에 콜백 지옥이 형성되지 않는다.
위의 코드를 보면 then, catch에서 다시 다른 then이나 catch를 붙였다.
이전 then의 return 값을 다음 then의 매개변수로 넘긴다.
Promise를 return한 경우 Promise가 수행된 후 다음 then이나 catch를 호출하게 된다.
Promise.all
Promise.all을 사용하면 Promise 여러 개를 한 번에 실행할 수 있다.
이전 예시는 모두 비동기 처리 여러 개를 직렬로 연결해 순차적으로 실행하였다면, Promise.all을 이용하면 비동기 처리 여러 개를 병렬로 처리 가능하다.
이때 iterable은 배열과 같이 반복 가능한 객체를 말한다.
위의 코드를 실행한 결과 remain은 [0, 500, 1000]으로 남게 될 것이다.
Promise.race
Promise.race는 가장 먼저 종료한 Promise 객체 결과만 다음 작업으로 보낼 때 사용한다.
Promise.race로 실행하면 A가 실행되고 남은 결과 값인 0만 반환될 것이다.
즉 buySomething("A", 500)이 가장 먼저 실행되며, 가장 먼저 종료되기 때문에 해당 작업의 결과 값만 반환된다.
하지만 나머지 작업도 실행은 될 것이다.
'Front-End > JavaScript' 카테고리의 다른 글