반응형

📝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

반응형