웹 개발

[포스코X코딩온] 비동기 처리

끊임없이 성장중인 개발자 2023. 11. 24. 01:29
728x90
반응형

포스팅 주제

  • 콜백(callback) 함수
  • Promise
  • Async / Await

 


 

비동기 처리란?

  • 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행 하는 JS 특성
  • 비동기 처리를 하는 이유
    1. 서버로 데이터 요청 시, 서버의 응답을 계속 기다리며 다음 코드를 실행 안하고 계속 기다리면 비효율 적이라
    2. 동기 처리시 코드 실행 -> Wait 을 반복하면 웹을 실행하는데 너무 오래 걸릴 수 있다.

 

콜백(CallBack)함수

 

콜백(CallBack)함수란?

  • 이벤트가 발생했을 때 실행되는 함수(다른 함수가 실행을 끝낸 뒤 실행되는 함수)
  • JS함수를 인자로 받고 다른 함수를 통해 반환될 수 있는데, 인자(매개변수)로 대입되는 함수
  • 함수 선언 시 parameter(인자, 매개변수)로 함수를 받는다.

 

그렇다면 콜백 함수를 왜 사용할까?

  • 비동기 방식로 작성된 함수를 동기 처리하기 위하여 사용한다.
  • 응답을 받은 이후 처리되어야 하는 종속적인 작업이 있을 수 있으므로 그에 대한 대응이 필요하다.

 

// ex) 편의점에 들어가서 음료수를 사고 나오는 상황
// 콜백 함수 X
function goMart(){
    console.log('마트에 와서 어떤 음료를 살지 고민중,,,')
}

function pickDrink(){
    // 3초 고민(3초 후 실행)
    setTimeout(function(){
        console.log('좋아 골랐어!')
        product = '콜라'
        price = 1600;
    }, 3000);
}

function pay(product, price){
    console.log(`상풍명 : ${product}, 가격 : ${price}`);
}

let product, price;
goMart();
pickDrink();
pay(product, price);
출력 영상

위 영상을 보시면 상품명에 product와 price가 undefinde로 출력되는 것을 확인할 수 있다.

왜냐하면 지금 pickdrink함수가 setTimeout함수로 선언되어 있어서 3초뒤에 실행되기 때문에 pay함수가 인자를 못받는 것이다.

 

이를 해결하기 위해서는 콜백함수를 사용해서 pay가 인자를 받기 위해서 pickDrink함수가 끝나기를 기다리게 만들어줘야 한다.

 

// ---------콜백 함수-----------

function goMart(){
    console.log('마트에 와서 어떤 음료를 살지 고민중,,,')
}

function pickDrink(callback){
    // 3초 고민(3초 후 실행)
    setTimeout(function(){
        console.log('좋아 골랐어!')
        product = '콜라'
        price = 1600;
        // 위에 코드가 실행된 이후 실행될 콜백함수
        callback(product,price) // 매개변수로 넘긴 콜백함수 실행
    }, 3000);
}

function pay(product, price){
    console.log(`상풍명 : ${product}, 가격 : ${price}`);
}

let product, price;
goMart();
pickDrink(pay); //pay라는 함수를 콤백 함수로 넘겨줌
// pay(product, price);
출력 영상
// ------------ 콜백 함수 2 -------------
function goMart(){
    console.log('마트에 와서 어떤 음료를 살지 고민중,,,')
}

function pickDrink(callback){
    // 3초 고민(3초 후 실행)
    setTimeout(function(){
        console.log('좋아 골랐어!')
        product = '콜라'
        price = 1600;
        // 위에 코드가 실행된 이후 실행될 콜백함수
        callback(product,price) // 매개변수로 넘긴 콜백함수 실행
    }, 3000);
}

function pay(product, price){
    console.log(`상풍명 : ${product}, 가격 : ${price}`);
}

let product, price;
goMart();
// 직접 안에다가 함수를 만들어도 된다.
pickDrink(function pay(product, price){
    console.log(`상풍명 : ${product}, 가격 : ${price}`);
});

 

다른 방식으로는 직접 함수를 집어 넣어도 된다.

 

Promise

 

콜백 지옥(CallBack Hell)

  • 비동기 프로그래밍 시 발생하는 문제로 콜백 함수가 반복되어 코드의 들여쓰기가 너무 깊어지는 현상이다.
  • 가독성이 떨어지고 코드 수정 난이도가 높아지는 단점이 있다.

 

이를 해결하기 위해서 Promise 형식을 사용하여 콜백 지옥을 줄일 수 있다.

 

Promise란?

  • 비동기 함수를 동기 처리하기 위해 만들어진 객체
  • 성공과 실패를 분리하여 반환
  • 비동기 작업이 완료된 후 다음 작업을 연결시켜 진행하는 기능을 가진다.

 

 

포스코x코딩온 교육자료

 

Promise는 두 가지 콜백 함수를 가진다

  • resolve(value): 작업이 성공(fulfilled)한 경우, 그 결과를 value와 함께 호출
  • reject(error): 에러(rejected) 발생 시 에러 객체를 나타내는 error와 함께 호출

 

resolve()는 then 메서드로 접근(실행)가능

reject()는 catch 메서드로 접근(실행)가능

 

// promise(프로미스) 객체
/**
 * - 비동기 함수를 동기처리하기 위해 만든 객체
 * - 미래에 성공 / 실패에 대한 결과 값을 '약속' 한다는 의미
 * - 성공과 실패를 분리해서 반환한다.
 * - 성공에 대한 결과는 then이라는 메서드로,
 * - 실패에 대한 결과는 catch라는 메서드로 처리
 * - resolved : 성공
 * - rejected : 실패
 */

//  1. promise 생성코드
function promise1(flag){
    // 프로미스 객체를 생성하여 반환
    return new Promise(function(resolved, rejected){
        if(flag){
            resolved(`현재 프로미스의 상태는 fulfilled(이행), then메서드로 연결! flag : ${flag}`);
        }else {
            rejected(`현재 프로미스의 상태는 rejected(거절), catch메서드로 연결! flag : ${flag}`);
        }
    })
}

// 2. promise 사용하는 코드
promise1(5 < 3).then((result) => {
    console.log('result :', result);
}).catch((error) => {
    console.log('error :', error);
});
출력 결과

 

// ----------- promise 예제 ------------
// index.js에서 "콜백함수"를 이용해서 동기처리한 코드를
// "promise"를 이용해 동기처리

function goMart(){
        console.log('마트에 와서 어떤 음료를 살지 고민중,,,')
    }
    
    function pickDrink(){
        return new Promise(function(resolved, rejected){
            // 3초 고민(3초 후 실행)
        setTimeout(function(){
            console.log('좋아 골랐어!')
            product = '콜라'
            price = 2600;
            const money = 2000;

            if(price < money){
                rejected();
            } else{
                resolved();
            }

            // resolved(); // 성공을 의미하는 resolve 실행
        }, 3000);
        })
    }
    
    function pay(product, price){
        console.log(`상풍명 : ${product}, 가격 : ${price}`);
    }
    
    let product, price;
    goMart();
    pickDrink().then(() => {
        pay(product, price);
    }).catch(()=>{
        console.log('돈이 모잘라요 ㅠㅠ');
    })
출력 결과

 

프로미스(promise) 체이닝

Promise 체이닝이란?

  • 여러개의 함수를 연속해서 묶어서 사용할 때 사용하는 것으로 .then을 연속으로 사용해서 사용가능
  • 에러는 catch하나로 에러를 제어가능

 

    // --------------------- 프로미스 체이닝 ----------------------

    // 함수를 이용해서 (4+3)*2-1 = 13 을 연산하기!

    // 1. 콜백 함수라면?
    function add(n1, n2, cb){
        setTimeout(function(){
            const result = n1 + n2;
            cb(result); // mul(result) 목적
        }, 1000);
    }
    function mul(n, cb){
        setTimeout(function(){
            const result = n*2;
            cb(result); // sub(result) 목적
        },700);

    }
    function sub(n, cb){
        setTimeout(function(){
            const result =n - 1;
            cb(result);
        },500)
    }

add(4,3,function(result1){
    console.log('add result :', result1);
    mul(result1, function(result2){
        console.log('mul result :', result2);
        sub(result2, function(result3){
            console.log('sub result : ', result3);
        })
    })
})

// 2. 프로미스 체이닝이라면?

function addPromise(n1, n2){
    return new Promise(function(resolv, reject){
        setTimeout(function(){
            const result = n1 + n2;
            resolv(result);
        }, 1000);

    })
}
function mulPromise(n){
    return new Promise(function(resolv, reject){
        setTimeout(function(){
            const result = n*2;
            // resolv(result);
            reject('의도적으로'); // 에러가 발생시 해당 문장을 출력하고 이후 중단된다.
        },700);
    })

}
function subPromise(n){
    return new Promise(function(resolv){
        setTimeout(function(){
            const result =n - 1;
            resolv(result);
        },500)

    })
}

addPromise(4,3).then(function(result1){
    console.log('add result :', result1);
    return mulPromise(result1);
}).then(function(result2){
    console.log('mul result :', result2);
    return subPromise(result2);
}).then(function(result3){
    console.log('sub result :', result3);
}).catch(function(err){
    console.log(err);
})
출력 결과

 

promise 체이닝을 사용하여 문장에 가독성을 올릴 수 있고, 에러 발생시 에러 문장을 출력 후 실행이 중단된다.

 

Promise 실습 1
// 실습1 callback -> promise
// function call(name, cb){
//     setTimeout(function(){
//         console.log(name);
//         cb(name);
//     },1000);
// }
// function back(cb){
//     setTimeout(function(){
//         console.log('back');
//         cb('back')
//     },1000)
// }

// function hell(cb){
//     setTimeout(function(){
//         cb("callback hell");
//     },1000);
// }

// call('kim', function(name){
//     console.log(name+'반가워');
//     back(function(txt){
//         console.log(txt+'을 실행했구나');
//         hell(function(message){
//             console.log('여기는'+message);
//         });
//     });
// });

// 
function call(name){
    return new Promise(function(resolve){
        setTimeout(function(){
            console.log(name);
            resolve(name);
        },1000);
    })
}
function back(){
    return new Promise(function(resolve){
        setTimeout(function(){
            console.log('back');
            resolve('back')
        },2000)
    })
}

function hell(){
    return new Promise(function(resolve){
        setTimeout(function(){
            resolve("callback hell");
        },1000);
    })
}

call('minsu').then(function(A){
    console.log(A + '반가워');
    return back();
}).then(function(B){
    console.log(B+'을 실행했구나');
    return hell();
}).then(function(C){
    console.log('여기는'+ C);
})

// 화살표 함수 버전
// call('minsu').then((A) => {
//     console.log(A + '반가워');
//     return back();
// }).then((B) => {
//     console.log(B+'을 실행했구나');
//     return hell();
// }).then((C) => {
//     console.log('여기는'+ C);
// })
출력 결과

 

 

Async / Await

Async / Await 이란?

  • Promise도 체이닝을 하다보면 then.then()... 처럼 코드가 길어지고 가독성이 떨어질 수 있다.
  • 가독성을 올리기 위해 Async / Await을 사용하여 Promise를 다르게 사용한다.

 

Async

  • 함수 앞에 붙여 Promise를 반환
  • 프로미스가 아닌 값을 반환해도 프로미스로 감싸서 반환

 

Await

  • 뜻 그대로 기다리라는 기능
  • 프로미스 앞에 붙여 프로미스가 다 처리될 때까지 기다리는 역할, 결과는 그 후에 반환

 

// async - await
// async : 함수 앞에다가 붙이는 키워드
// - 함수만 보고도 비동기 함수임을 알 수 있다.
// - 프로미스를 반환

// await : 기다린다.
// - 성공 / 실패로 프로미스 객체의 실행이 완료되기를 기다려줌
// - await 뒤에는 프로미스가 오게 됨
// async - await 짝궁, async 키워드를 사용한 함수 안에서만 await 사용가능 

function goMart(){
        console.log('마트에 와서 어떤 음료를 살지 고민중,,,')
    }
    
function pickDrink(){
    return new Promise((resolve, reject) => {

        setTimeout(function(){
            console.log('좋아 골랐어!')
            product = '콜라'
            price = 1600;
            reject('에러 발생!!!');
            // resolve();
        }, 3000);
    })
}
    
function pay(product, price){
        console.log(`상풍명 : ${product}, 가격 : ${price}`);
}

// let product, price;
// async function exec(){
//     goMart();
//     await pickDrink();
//     pay(product, price);
// }
// exec();

// async-await 키워드 사용시, 에러처리는 try-catch문으로!

async function exec1(){
    try{
        goMart();
        await pickDrink();
        pay(product, price);
    }catch(error){
        console.log(error);
    }
    }
exec1();

// then - catch를 쓰면
// goMart();
// pickDrink().then((result)=>{
//     console.log(result);
// }).catch((error) => {
//     console.log(error);
// })

 

에러 처리는 try-catch문을 사용해서 처리 가능하다.

 

출력 결과

 

Promise 실습 2
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

</head>
<body>
    <h1>콜백지옥 예시</h1>
    <script>
    //     setTimeout(function(){
    //         document.body.style.backgroundColor = 'red';

    //         setTimeout(function(){
    //         document.body.style.backgroundColor = 'orange';

    //         setTimeout(function(){
    //         document.body.style.backgroundColor = 'yellow';

    //         setTimeout(function(){
    //         document.body.style.backgroundColor = 'green';

    //         setTimeout(function(){
    //         document.body.style.backgroundColor = 'blue';

    //     }, 1000)
    //    }, 1000)
    //   }, 1000)
    //  }, 1000)
    // }, 1000)
    </script>
    <script>
        function display(color){
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    document.body.style.backgroundColor = color;
                    resolve();
                }, 1000);
            });
        }

        // async await 방식

        async function exec(){
            await display('red');
            await display('orange');
            await display('yellow');
            await display('green');
            await display('blue');
        }
        exec();

        // then 이용

        // display('red').then(function(){
        //     return display('orange');
        // }).then(function(){
        //     return display('yellow');
        // }).then(function(){
        //     return display('green');
        // }).then(function(){
        //     return display('blue');
        // })

    </script>
</body>
</html>

 

출력 결과

 

반응형