📝Promise
비동기 처리를 도와주는 객체로서 콜백지옥의 문제를 깔끔하게 해결해줄 수 있다 → Promise Chainning 기술을 이용
물론 남발하면 콜백 지옥이 다시 시작된다 → 잘 활용 해야한다
Promise 상태
- Pending
- 수행이 끝나지 않은 상태 → resolve 값이 반환이 안 된 상태
- Reject
- 수행을 시켰지만 실패된 상태
- Fulfilled
- 수행이 올바르게 끝난 상태
Promise 안에 비동기(일반적으로 Ajax, Fetch, Axios 등...)에 대한 내용 및 성공했을 때 반환할 내용인 Resolve와 실패했을 때 반환할 내용인 Reject가 들어가며 Then()을 이용해 비동기 내용을 실행시킨다 → Promise 선언시 바로 동작 되기 때문에 Function안에 사용하는게 일반적이다.
Promise 사용 방법 (기초)
// Promise (Async operation)
// - get status (pending → fufilled or rejected)
// - Producer, Consumer
/** ───── 1. Producer (Promise 객체는 선언시에 자동적으로 실행된다.) ───── **/
const promise = new Promise((resovle, reject) => { // 기본 문법 new Promise((resolve, reject) => ...
console.log('doing something...'); // doing some heavy work
// setTimeout(() => resovle('ellie'), 2000) // 성공시에는 ellie라는 값 return
setTimeout(() => reject(new Error('no network')), 2000) // 실패시 no network라는 값 return
});
/** ───── 2. Consumer (then, catch, finally) ───── **/
promise.then(value => console.log(value)) // 성공시 ellie 출력 (then은 promise을 실행한다를 의미)
.catch(error => console.log(error)) // 실패시 no network 출력 (Error 핸들링은 catch 사용)
.finally(() => console.log('finally')) // 성공하든 실패하든 finally 출력
/** ───── 3. Promise chaining ───── **/
const fetchNumber = new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000); // return 값 : 1
});
fetchNumber
.then((num) => num * 2) // return 값 1을 파라미터로 1 * 2 값을 return (resolve)
.then((num) => num * 3)
.then((num) => {
return new Promise((resovle, reject) =>
setTimeout(() => resovle(num -1), 1000)) ;
}) // promise를 return 가능
.then((num) => console.log(num)); // then 을 쓰려면 resolve가 있어야한다. (then 쓰고 핸들링이 있을 경우인 거 같다)
콜백지옥 (URL 통신)
/**
@author 이성재
@why 엔진 기동
*/
let turnOn = () => {
if (confirm("검색 엔진을 기동하시겠습니까?") == true) {
// 패스워드 체크
let adminPw = $("#admin_pw").val();
let id = "testid";
let shaAdminPw = hex_sha512(adminPw).toString();
return new Promise((resolve, reject) => {
$.ajax({
type: "GET",
url: "./check/password", // hecking
data: {
shaAdminPw: shaAdminPw, // 암호화 password
id: id, // id
type: 'f' // ?
},
success: (data) => {
if (data !== "success") return "비밀번호가 틀렸습니다";
if (data === "success") {
// 검색엔진 기동
$.ajax({
url: './turn-on',
type: 'GET',
success: () => { return "검색 엔진이 기동되었습니다."},
error: () => { return "통신 오류가 발생했습니다."}
}); // ajax end
}
},
error : () => { return "패스워드 체크에 실패했습니다.";}
}); // ajax 종료
});
}else {
return false;
}
}
Ajax Promise Chaning으로 콜백 지옥 해결 (URL 통신)
/**
@author 이성재
@why 엔진 기동 요청
*/
let turnOnAjax = () => {
return new Promise((resolve,reject) => {
$.ajax({
url: './turn-on',
type: 'GET',
success: () => {resolve("검색 엔진이 기동되었습니다.")},
error: () => {reject("통신 오류가 발생했습니다.")}
}); // ajax end
}); // promise end
}
/**
@author 이성재
@why 엔진 재기동에 필요한 패스워드 검사
*/
let checkPasswordAjax = () =>{
let adminPw = $("#admin_pw").val();
let id = "testid";
let shaAdminPw = hex_sha512(adminPw).toString();
return new Promise((resolve, reject) => {
$.ajax({
type: "GET",
url: "./check/password", // hecking
data: {
shaAdminPw: shaAdminPw, // 암호화 password
id: id, // id
type: 'f' // ?
},
success: (data) => {
if (data == "success") resolve("success");
else resolve("비밀번호가 틀렸습니다");
},
error : () => {reject("패스워드 체크에 실패했습니다.");
}
}); // ajax 종료
});
}
/**
@author 이성재
@why 엔진 기동
*/
let turnOn = () => {
if (confirm("검색 엔진을 기동하시겠습니까?") == true) {
// 패스워드 체크
checkPasswordAjax()
.then((message) => {
if(message === "success") return turnOnAjax();
else alert(message); // 패스워드 틀림
})
.catch((errorMsg) =>{
alert(errorMsg);
})
// 엔진 기동
.then((message) => {
g_showLayer();
alert(message);
location.reload()
})
.catch((errorMsg) =>{
alert(errorMsg);
})
.finally(() => g_closeLoading)
}else {
return false;
}
}
📝Async
Promise는 비동기 처리를 좀 더 가독성 및 여러가지 측면에서 효율적으로 만들어주지만 그에 대한 코드가 너무 길어서 지저분에 보일 수 있다 Async는 이를 보완해준다 Async 키워드는 Function 앞에 사용하며 해당 함수는 항상 프라미스를 반환된다
Asyn 기본 예제
// 1. async
async function fetchUser() {
return 'ellie'; // async에서 return = resolve와 등치
}
const user = fetchUser();
user.then(console.log); // Promise {[[PromiseState]]: 'fulfilled', [[PromiseResult]]: 'ellie', Symbol(async_id_symbol): 5, Symbol(trigger_async_id_symbol): 1}
console.log(user); // ellie
Async 기본예제2
function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple(){
await delay(1000);
return 'apple';
}
async function getBanana(){
await delay(1000);
return 'banana';
}
function getBananaPromise(){ // getBanana Promise Version (getBanana()와 등치)
return delay(1000)
.then (()=>'bananaPromise')
}
let apple = getApple(); // promise pending 상태
apple.then(console.log); // apple
getBanana().then(console.log) // banana
getBananaPromise().then(console.log); // bananaPromise
📝Await
비동기 통신이 순차적으로 이루어져야하는 경우 또는 비동기 통신한 값을 변수에 담고 싶은 경우 사용할 수 있다.
Await는 말 그대로 통신을 기다리는 것이고 Async 안에서만 사용이 가능하다 → Await는 Then()을 포함해 실행시킨다
Await 예제
function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple(){
await delay(1000);
return 'apple';
}
async function getBanana(){
await delay(1000);
return 'banana';
}
async function pickFruits(){
const apple = await getApple(); // 1초
const banana = await getBanana(); // 1초
return `${apple} + ${banana}`
} // Total : 2초
pickFruits().then(console.log) // apple + banana
위와 같은 경우는 실행시 getApple()로 비동기 객체(Promise)를 가져오면서 실행이 됩니다.
Await의 역할은 Promise 상태가 fufilled가 될때까지 기다립니다 그리고 그 결과 값을 변수에 할당해줄 수 있습니다.
- getApple을 통해 Promise를 가져오고 (pending 상태) await을 거쳐 1초 기다리고 Apple을 반환
- getBanana을 통해 Promise를 가져오고 (pending 상태) await을 거쳐 1초 기다리고 Banana를 반환
총 2초가 걸리게 됩니다 이렇게 할 경우 순차적으로 처리가 가능하지만 병렬로 처리가 안 되기 때문에 낭비되는 시간이 생깁니다 그럴 경우 이런식으로 처리가 가능합니다.
Await 개선
function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple(){
await delay(1000);
return 'apple';
}
async function getBanana(){
await delay(1000);
return 'banana';
}
async function pickAllFruits(){
const applePromise = getApple(); // promise 객체 (pending)
const bananaPromise = getBanana(); // promise 객체 (pending)
const apple = await applePromise // 실행 후 대기
const banana = await bananaPromise // 실행 후 대기
return `${apple} + ${banana}`
}
pickAllFruits().then(console.log) // apple + banana
- applePromise = getApple()
- bananaPromise = getBanana()
이 두개를 통해 각각 비동기로 먼저 호출해서 결과값을 가진 (fulfilled) Promise 를 만들도록 한 뒤 await을 통해 fullfilled 가 된 경우 그 결과값이 apple과 banana로 들어가게 됩니다 그래서 1초만에 apple + banana가 출력이 되게 됩니다.
📝Promise.all
Await개선에 있는 코드처럼 쓰는 경우 뭔가 중복된 느낌이 강합니다. 이 부분을 개선한 방식이 Promise.all입니다.
Promise.all의 경우 all에 등록한 Promise객체가 전부 완료될때까지 기다려줍니다
가독성 해치는 코드
// 가독성을 해침
async function pickAllFruits(){
const applePromise = getApple(); // promise 객체 (pending)
const bananaPromise = getBanana(); // promise 객체 (pending)
const apple = await applePromise // 실행 후 대기
const banana = await bananaPromise // 실행 후 대기
return `${apple} + ${banana}`
}
Promise.all로 개선
function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple(){
await delay(1000);
return 'apple';
}
async function getBanana(){
await delay(1000);
return 'banana';
}
function pickFruits(){
return Promise.all([getApple(),getBanana()]) // promise 객체
.then(fruits => fruits.join(' + '));
}
pickFruits().then(console.log); // apple + banana
📝Promise.race
race는 말 그대로 경주를 한다는 의미입니다. 등록된 Promise 객체중에서 먼저 종료되는 Promise만 리턴해줍니다.
Promise.race
function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getBananaOne(){
await delay(1000);
return 'banana';
}
async function getBananaTwo(){
await delay(2000);
return 'banana2';
}
async function getAppleOne(){
await delay(1000);
return 'apple';
}
async function getAppleTwo(){
await delay(2000);
return 'apple2';
}
function pickOnlyOne1() {
return Promise.race([getAppleTwo(), getBananaOne()]);
}
function pickOnlyOne2() {
return Promise.race([getAppleOne(), getBananaTwo()]);
}
pickOnlyOne1().then(console.log); // banana
pickOnlyOne2().then(console.log); // apple
Arrow Function을 이용한 async 표현법
// Arrow Function을 이용한 async 표현법
const getDog = async () => {
await sleep(1000);
return '멍멍이';
};
fetch을 이용해 json 데이터 받아오기
const fetching = fetch('https://dummyjson.com/products/1'); // fetch에 대한 내용이 들어간 Promise
const response = await fetching; // Promise 실행시킨 Response 객체 내용
// response.then()과 동일한 역할이지만 데이터를 받아온 후에 처리하려면 response.then((res)=>res.json()) 이런식 처리 필요
const data = await response.json(); // response에서 응답값 결과
🔗 참고 및 출처
https://www.youtube.com/watch?v=aoQSOZfz3vQ&t=736s