전 포스팅에서 비동기와 콜백에 대해서 살펴봤는데, 순차적으로 데이터를 받아오고, 뭔가 처리하는 과정에서 콜백 함수가 남발되고 가독성이 떨어지고, 애러가 발생하거나 디버깅을 할 때 처리하기가 힘들어진다.
이러한 문제를 해결하기 위해 여러 라이브러리들이 등장했고, 그 중에서 개발자들에게 널리 선택받은 것이 바로 Promise 패턴을 사용한 라이브러리들(jQuery Deffered, Q, Bluebird)이다. 이 라이브러리들이 표준화되어, 결국 ES2015에 이르러 JavaScript 언어 자체에 포함되게 되었다.
🔸 promise는 자바스크립트 비동기 처리에 사용되는 객체이다. Promise 객체가 만들어 지는 시점에는 그 안에 무엇이 들어갈 지 모를 수도 있다. 대신 then
메서드를 통해 콜백을 등록해서 작업이 끝났을 때 결과앖을 가지고 추가 작업을 할 수 있다.
const $= require('jquery'); console.log(1) function getData(callback){ console.log(3) $.get('https://jsonplaceholder.typicode.com/users',function(res){ console.log(4) callback(res) }) } getData(function(data){ console.log(2) console.log(data) })
이렇게 AJAX로 데이터를 받아올 경우,
200명의 user의 정보가 들어있는 API를 가공해, 새로운 값을 넣고 body에 나타나게 하려면 콜백함수도 많이 사용되고 코드실행도 많이됨.
getData('https://jsonplaceholder.typicode.com/todos',function(data){
const arr= data.filter(user => user.userId === 10)
arr.map(user => {
if(user.completed){
const newUser= {...user,pass:true}
const elem= $(`<p>${newUser.id} ${newUser.title}</p>`)
return document.body.append(elem[0])
}
})
})
- Pending(대기) => new Promise() 호출
- Fulfilled(이행) =>function(resolve,reject){resolve()}
- Rejected(실패) => function(resolve,reject){reject()}
const $= require('jquery');
function getData(){
return new Promise(function(resolve,reject){
//pending
$.get('https://jsonplaceholder.typicode.com/todos',function(res){
if(res) resolve(res);
//Fulfilled
reject(new Error('Faild!'))
//rejected
})
})
}
// $.get()의 호출결과에따라 data / error 출력
getData().then(function(data){
console.log(data)
}).catch(function(err){
console.log(err)
})
콜백 Version
function print(ms) {
return console.log(`print${ms}`);
}
function timer(ms, callback) {
setTimeout(callback, ms);
}
timer(1000, () => {
print(1000);
timer(2000, () => {
print(2000);
timer(3000, () => {
print(3000);
timer(4000, () => {
console.log("finish");
});
});
});
});
// print1000 -> print2000 -> print3000 -> finish
Promise Version
function print(ms) {
return console.log(`print${ms}`);
}
function timer(callback) {
return new Promise(function (resolve, reject) {
setTimeout(() => {
callback;
resolve();
}, 1000);
});
}
timer(print(1000))
.then(() => timer(print(2000)))
.then(() => timer(print(3000)))
.then(() => {
console.log("finish");
});
.then()
메서드를 사용하면 ms를 다시 지정할 필요 없이, timer()가 호출된 뒤 순차적으로 then()을 타고 가므로 콜백함수만 넣어주면 된다. 짱편함.
getData().then(function(result) {
console.log(result); // hi
throw new Error("Error in then()");
}).catch(function(err) {
console.log('then error : ', err); // then error : Error: Error in then()
});
const sunny = () => new Promise((resolve, reject) => { setTimeout(() => resolve(`🌤`), 1000); }); const cloud = (sun) => new Promise((resolve, reject) => { setTimeout(() => resolve(`${sun} => 🌦`), 1000); });; const rain = (sun) => new Promise((resolve, reject) => { setTimeout(() => resolve(`${sun} => 🌧`), 1000); }); ; sunny() .then(sun => cloud(sun)) .then(sun => rain(sun)) .then(result => console.log(result)).catch(err =>console.log(err)) // 🌤 => 🌦 => 🌧
🔸 애러가 발생한다면 들어갈 코드 중간에 끼어넣으면 error 메세지가 끼지 않고 대체됨.
const cloud = (sun) =>
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error(`${sun} => 🌦`)), 1000);
});;
sunny()
.then(sun => cloud(sun))
.catch(err =>`🌩`)
.then(sun => rain(sun))
.then(result => console.log(result))
//🌩 => 🌧
- state : resolve / reject
- Producer(new Promise) vs Consumer(then,catch,finally)
그래서 클릭해야만 통신해야 되는 경우 주의해야 함.
const promise1 =() => {
new Promise((resolve,reject) => {
console.log('click Promise')
})
}
document.getElementById('btn').addEventListener('click',promise1)
- then(정상적으로 처리되었을 때 실행될 메서드, 인자 : 결과값)
- catch(애러가 났을 때 실행될 메서드, 인자:error)
- finally (결과값에 상관없이 실행되는 메서드, 인자 : 없음)
const promise = new Promise((resolve, reject) => { setTimeout(() => { // resolve("eunsu"); reject(new Error('find error')) }, 2000); }); promise .then((result) => console.log("then")) .catch((err) => console.log(err)) .finally(() => console.log("finally"));
◾ error는 자바스크립트에서 제공하는 오브젝트인 new Error를 사용해서 애러를 잡음.