TIL

TIL 240821 (콜백 함수 2)

j-coder 2024. 8. 21. 22:22

2. 콜백 지옥과 비동기 제어

 

2 - 1 콜백지옥

콜백함수를 익명 함수로 전달하는 과정이 계속 반복되는 경우를 말한다.

주로 통신과 비동기적 작업을 할때 발생한다.

콜백지옥은 가독성이 안좋아서 수정하기도 어렵다.

 

2 - 2 동기와 비동기

동기 : 실행중인 코드가 완료되기까지 기다렸다가 다음 코드로 넘어가는것

비동기 : 실행중인 코드가 완료되지 않아도 실행된 후에  바로 다음 코드로 넘어가는것

 

 

 

통신이 들어간 코드는 보통 비동기적 코드이다.

 

비동기적 코드 예시

setTimeout(function(){

    console.log('1')

}, 1000);


console.log('2');

 

이 코드는 비동기적 코드로 2 먼저 출력되고 1이 출력된다. (기다리지 않는다)

 

2 - 3 콜백지옥 해결방안

 

예시)

값 전달순서 : 아래 ㅡ> 위

setTimeout(

    function (name) {

        var icecreamList = name;

        console.log(icecreamList);


        setTimeout(

            function (name) {

                icecreamList += ", " + name;

                console.log(icecreamList);


                setTimeout(

                    function (name) {

                        icecreamList += ", " + name;

                        console.log(icecreamList);


                        setTimeout(

                            function (name) {

                                icecreamList += ", " + name;

                                console.log(icecreamList);

                            },

                            500,

                            "메로나"

                        );

                    },

                    500,

                    "바밤바"

                );

            },

            500,

            "누가바"

        );

    },

    500,

    "죠스바"

);

 

해결방안 )

 

기명함수로 변환

 

let coffeeList = '';


let addice1 = function (name) {

    icecreamList = name;

    console.log(icecreamList);

    setTimeout(addice2, 500, '메로나');

};


let addice2 = function (name) {

    icecreamList += ', ' + name;

    console.log(icecreamList);

    setTimeout(addice3, 500, '바밤바');

};


let addice3 = function (name) {

    icecreamList += ', ' + name;

    console.log(icecreamList);

    setTimeout(addice4, 500, '누가바');

};


let addice4 = function (name) {

    icecreamList += ', ' + name;

    console.log(icecreamList);

};


setTimeout(addice1, 500, '죠스바');

 

가독성은 좋은데 이름을 하나하나 붙이는게 불편하다. 그래서 이 코드는 완전한 해결방안이 아니다.

ES6문법인 Promise, Generator와 ES7문법인 async/await 같은 것들로 비동기 작업의 동기적 표현이 필요하다.

 

2 - 4 비동기 작업의 동기적 표현

비동기적 코드는 순서를 보장하지 않는다.

 

Promise : 비동기 처리 중 처리가 끝나면 알려준다.( 성공 : resolve, reject = 실패)

  • new로 호출한 Promise의 인자로 넘어가는 콜백은 바로 실행돼요.
  • resolve 또는 reject 함수를 호출하는 구문이 있을 경우 둘 중 하나가 실행되기 전까지는 다음(then), 오류(catch)로 가지 않는다.

Promise (1)

new Promise(function(resolve){

    setTimeout(function(){

        let name = "죠스바";

        console.log(name);

        resolve(name);

    }, 500);

}).then(function(prevName){

    // 여기도 새 Promise를 만든다

    return new Promise(function(resolve){

        setTimeout(function(){

            let name = prevName + ", 메로나";

            console.log(name);

            resolve(name);

        }, 500);

    });

}).then(function(prevName){

    // 여기도 새 Promise를 만든다

    return new Promise(function(resolve){

        setTimeout(function(){

            let name = prevName + ", 바밤바";

            console.log(name);

            resolve(name);

        }, 500);

    });

}).then(function(prevName){

     // 여기도 새 Promise를 만든다

    return new Promise(function(resolve){

        setTimeout(function(){

            let name = prevName + ", 누가바";

            console.log(name);

            resolve(name);

        }, 500);

    });

});

 

Promise (2)

Promise (1)의 반복부분 코드를 함수화 한 코드이다.

 

// let addIce = (name) => {}      //  화살표 함수

let addIce = function (name) {    

    return function(prevName){

        // 여기도 새 Promise를 만든다

        return new Promise(function(resolve){

            setTimeout(function(){

                // 백틱 ``

                // if (newName) {


                // }else {


                // }

                let newName = prevName ? `${prevName}, ${name}` : name;

                console.log(newName);

                resolve(newName);

            }, 500);

        });

    };

};

 // then (그러면 ~~)

addIce("죠스바")()

    .then(addIce("메로나"))

    .then(addIce("바밤바"))

    .then(addIce("누가바"));

 

 

Generator

Generator(함수)를 실행하면 iterator(반복가능) 객체를 반환한다.

순서가 필요한 로직에서 순서보장을 받기위해 비동기적인 요소를 동기적으로 바꾸려고 한다.

 

// (1) 제너레이터 함수 안에서 쓸 addIce 함수 선언

let addIce = function (prevName, name) {

    setTimeout(function () {

        iceMaker.next(prevName ? prevName + ', ' + name : name);

    }, 500);

};



// *이 붙은 함수가 제너레이터 함수

// 실행하면 -> Iterator 객체가 반환된다.

// (2) 제너레이터 함수 선언

// yield 키워드로 순서 제어

let iceGenerator = function* () {

    var ice1 = yield addIce('', '죠스바');

    console.log(ice1);

    var ice2 = yield addIce(ice1, '메로나');

    console.log(ice2);

    var ice3 = yield addIce(ice2, '바밤바');

    console.log(ice3);

    var latte = yield addIce(ice3, '누가바');

    console.log(latte);

};

let iceMaker = iceGenerator();


iceMaker.next();

 

 

Promise + [async (비동기), await(기다리다)]

Promise ~ then 과 동일한 효과이다.

async 함수 내부에서 실질적인 비동기 작업이 필요한 위치마자 await를 넣어주면 된다.

// iceMaker 함수에서 호출할 함수, 'addICe'를 선언

// Promise를 반환

var addICe = function (name) {

    return new Promise(function (resolve) {

        setTimeout(function(){

            resolve(name);

        }, 500);

    });

};


// var iceMaker = async () => {}  ㅡ>  화살표 함수

var iceMaker = async function () {

    var iceMaker = '';

    var _addICe = async function (name) {

        iceMaker += (iceMaker ? ', ' : '') + await addICe(name);

    };


    // Promise를 반환하는 함수인 경우, awit를 만나면 무조건 끝날 때 까지 기다린다.

    // _addICe('죠스바') 이 로직이 실행되는데 N초가 걸렷다.

    await _addICe('죠스바');


    // console.log는 N초 뒤 실행

    console.log(iceMaker);



    await _addICe('메로나');

    console.log(iceMaker);

    await _addICe('바밤바');

    console.log(iceMaker);

    await _addICe('누가바');

    console.log(iceMaker);

};


iceMaker();