Promise
기존 callback함수를 통한 비동기처리를 개선하기 위한 문법
producer/consumer 코드를 연결
- producer(비동기처리가 포함된 코드)
- consumer(비동기완료 후 실행할 코드)
속성
- status
- pending -> fulfilled(이행) / rejected(거부)
- result
- undefined -> value(이행할 경우) / error 객체 (거부될 경우)
resolve : 이행시 호출할 콜백함수 (then에서 전달) - 실행시 PromiseState : fulfilled / PromiseResult : value
reject : 거부시 호출할 콜백함수 (catch에서 전달) - 거부시 PromiseState : rejected / PromiseResult : error
btn1.onclick = () => {
// resolve 이행시 호출할 콜백함수. then에서 전달.
// reject 거부시 호출할 콜백함수. catch에서 전달.
const promise = new Promise((resolve, reject) => {
const num = Math.floor(Math.random() * 2); // 0 1
console.log("producing.....", num);
try {
if(!num) throw new Error("오류 발생");
resolve(num); // 이행 콜백호출
} catch(e){
reject(e); // 거부 콜백 호출
}
});
console.log(promise);
promise
.then((value) => {
console.log('result : ', value);
})
.catch((err) => {
console.error('result : ', err);
});
};
먼저 프로미스 객체를 만들고 프로미스 객체 안에 resolve, reject 를 인자로 가지는 callback 함수를 만든다.
resolve : 이행시 호출할 콜백함수 (then에서 전달) - 실행시 PromiseState : fulfilled / PromiseResult : value
reject : 거부시 호출할 콜백함수 (catch에서 전달) - 거부시 PromiseState : rejected / PromiseResult : error
다음과 같은 결과가 나올 것으로 예상이 된다.
실행시 다음과 같은 코드가 나오고
거부시 다음과 같은 코드가 나오게 된다.
setTimeout을 Promise로 작성하기
btn2.onclick = () => {
getTimeoutPromise("안녕", 2000)
.then((value) => {
alert(value);
// then은 암묵적으로 promise객체를 만들어 반환 (promise chain)
})
.then(() => {
alert("실행 끝~");
});
};
const getTimeoutPromise = (msg, millis) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(msg);
}, millis);
});
};
then은 암묵적으로 promise 객체를 만들어서 반환하기때문에 2초후 안녕 - 실행끝 이 나오게된다.
getTimeoutPromise 안 resolve에서 메세지인 "안녕"을 받았기 때문에 then의 value에서 그 값을 받고, alert로 출력되는 구조이다.
Promise Chain
- 암묵적 chain : then의 resolve 콜백에서 리턴값이 없다면, 암묵적으로 promise 객체 반환
- resolve 콜백에서 특정값을 리턴한다면, 암묵적 promise 객체의 result 값으로 사용된다.
- 명시적 chain : then의 resolve 콜백에서 다시 promise객체를 반환 후 then 메소드로 chaining 가능
btn3.onclick = () => {
getTimeoutPromise("안녕", 1000)
.then((msg) => { //then(1)
console.log(msg);
return getTimeoutPromise("잘가", 1000);
})
.then((msg) => { //then(2)
alert(msg);
});
};
const getTimeoutPromise = (msg, millis) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(msg);
}, millis);
});
};
바깥 getTimeoutPromise에서 "안녕"을 받아서 then(1)이 1초 뒤 콘솔에 출력하고
return의 getTimeoutPromise에서 "잘가"를 받아서 아래의 then(2)에서 출력한다
실습문제 - 배경색 변경하기
. bg-box 배경색을 1초단위로 빨-초-노-파-핑 으로 변경
btn4.onclick = () => {
changeBgcolorPromise('red', 1000)
.then(() => changeBgcolorPromise('green', 1000))
.then(() => changeBgcolorPromise('yellow', 1000))
.then(() => changeBgcolorPromise('blue', 1000))
.then(() => changeBgcolorPromise('pink', 1000))
};
const changeBgcolorPromise = (color, millis) => {
const target = document.querySelector(".bg-box");
return new Promise((resolve) => {
setTimeout(() => {
target.style.backgroundColor = color;
resolve();
}, millis);
});
}
먼저 .bg-box의 배경색을 millis이후에 color로 변환하는 프로미스를 선언한다 (=changeBgcolorPromise)
그 후 changeBgcolorPromise를 호출하고, then에서 받고 , then에서 다시 호출하고, 받고 를 반복한다.
다음과 같은 결과가 나오게 된다.
js파일 로드
btn5.onclick = () => {
loadScriptPromise('js/1.js')
.then(() => {
const path = bar(); // js/2.js
return loadScriptPromise(path);
})
.then(() => {
const path = car();
return loadScriptPromise(path);
})
.then(() => {
dar();
});
};
const loadScriptPromise = (path) => {
return new Promise((resolve) => {
const script = document.createElement("script");
script.src = path;
script.onload = () => {
console.log(`${path} 로딩완료!`);
resolve();
};
document.head.append(script);
});
};
//1.js
const bar = () => {
console.log("bar");
return 'js/2.js';
};
//2.js
const car = () => {
console.log("car");
return "js/3.js";
};
//3.js
const dar = () => {
console.log("dar");
};
버튼 클릭시 로딩이 동시에 진행된다.
return한 값이 path로 들어가서 다음 값을 계속 불러오는 구조이다.
action이 랜덤으로 일어나는 코드 만들기
const devActions = {
gotocafe : {
actionname : '카페로 출발합니다.',
duration : 1000,
next : 'arriveatcafe'
},
arriveatcafe : {
actionname : '카페에 도착했습니다.',
duration : 100,
next : 'startcoding'
},
startcoding : {
actionname : '코딩을 시작합니다.',
duration : 3000,
next : 'finishcoding'
},
finishcoding : {
actionname : '코딩을 마칩니다.',
duration : 10,
next : 'gotohome'
},
gotohome : {
actionname : '집으로 출발합니다.',
duration : 1000,
next : 'gethome'
},
gethome : {
actionname : '집에 도착했습니다.',
duration : 0
},
}
btn6.onclick = () => {
runDevAction('gotocafe')
.then((action) => runDevAction(action))
.then((action) => runDevAction(action))
.then((action) => runDevAction(action))
.then((action) => runDevAction(action))
.then((action) => runDevAction(action))
.catch((err) => console.error(err))
.finally(() => {
console.log('해가 지고, 하루가 끝났습니다.');
});
};
const runDevAction = (action) => {
const {actionname, duration, next} = devActions[action];
console.log(actionname);
return new Promise((resolve, reject) => {
Math.random() * 100 > 90 && reject(new Error('친구에게 전화가 왔습니다.'));
setTimeout(() => {
resolve(next);
}, duration);
});
}
runDevAction 는 action의 종류, 유지시간, 다음 action을 지정한다.
Promise 객체에서 랜덤으로 다음 액션을 지정하고, setTimeout 으로 일정 시간이 지난 다음, 다음 액션이 일어나도록 resolve에 다음 액션을 입력받는다.
버튼을 클릭하면 gotocafe에서 부터 then으로 다음 액션들이 실행된다. 만약 error(친구에게 전화가 온 경우) 가 발생할 경우 error창을 받도록 catch절도 넣었다.
다음과 같이 다양한 경우가 발생하는 것을 확인할 수 있다.
Promise Chain - 값 반환
btn7.onclick = () => {
new Promise((resolve) => {
resolve(1); //producing
})
.then((value) => { //consumer
console.log(value); // 1
return value * 2; // 암묵적 Promise객체의 resolve(value)의 result(value)값이 된다.
})
.then((value) => {
console.log(value); // 2
return value * 2;
})
.then((value) => {
console.log(value); // 4
return value * 2;
})
.then((value) => {
console.log(value); // 8
return value * 2;
});
};
return값은 암묵적 프로미스 객체의 resolve(value) 의 결과가 된다.
return값이 *2 이므로 두배씩 증가하는 값이 나온다는 사실을 예상할 수 있다.
producing 으로 resolve값 1 이 생기면 then에서는 resolve값을 받아서 사용하게된다 (consumer)
생산자와 사용자가 연결된 좋은 코드라고 볼 수 있다.
다음과 같은 결과가 나오게 된다.