공군소령김도지
624
2021-07-04 09:02:07
5
206

(JS) 왜 콜백 함수를 쓰면 비동기적 처리방식에서 오는 오류가 안생기나요?(setTimeout)


function findUser(id){
    let user;

    setTimeout(function(){
        console.log("wait 2 sec");

        user = {
            id:id
        };

    },2000);
    
    return user;
}

 console.log(findUser(1))


위의 코드를 보시면 setTimeout은 Web API 이기 때문에

자바스크립트 콜스택에 있는것들( console.log(findUser(1))  )이 전부 출력된 후에

콜스택에 포함되어 로그창에 결과가 

undefined

wait 2 sec

이렇게 뜬다고 알고 있습니다. 


위의 오류를 해결하기 위해선 

function findUserCallBack(id,cb){
    setTimeout(function(){

        console.log("wait 2 sec")
        const user = {
            id:id
        };

        cb(user);

    },2000)
}

findUserCallBack(1,function(user){
    console.log(user)
})


setTimeout의 콜백 함수 안에 또 함수를 넣어서

wait 2 sec

{ id: 2 }

이렇게 옳게 출력 하더라고요....



왜 setTimeout 콜백함수 안에다가 cb(user) 즉 함수를 호출하니

에러가 해결되는지 모르겠습니다. 그리고 출력이 어떤 순서로 이뤄지는 가르켜 주실수 있나요?

setTimeout 함수가 콜백큐를 갔다온후 콜스택에서 출력이 된뒤에 findUserCallBack함수가 출력 되는 건가요?


0
  • 답변 5

  • 메이플비
    452
    2021-07-04 09:36:50 작성 2021-07-04 09:38:55 수정됨

    에러가 해결되었다고 하기는 좀 의미가 안 맞고, 라인이 읽히는 순서가 바뀐거죠.

    첫번째는

    let user를 먼저 선언을 하고,

    타임아웃은 비동기니까 이벤트 루프에 넣어버리고 다음줄인 리턴을 읽습니다. 당연히 비동기에서 아직 값을 주지 않았으므로 언디파인드겠죠. 그래서 콘솔로그가 리턴값으로 언디파인드를 받아서 언디파인드를 출력하고, 2초후 비동기로 실행된 타임아웃에 넣은 콜백이 실행되어서 출력되는거구요.


    두번째는 콜백함수가 타임아웃 안으로 들어갔기 떄문에 타임아웃의 코드가 차례로 실행되어서 마지막에 콜백을 호출해서 user정보를 넘겨주고, 그 user정보를 콘솔로그 하게 되는거죠

  • 공군소령김도지
    624
    2021-07-04 10:25:52

    ㄴ 그러니까 두번째 예제는 setTimeOut안에 있는 cb 함수까지 같이 이벤트 루프를 탔다가 


    cb( ) 

    console.log()

    setTimeOut


    스택에 위와 같이 쌓여서 출력 되는건가요? 근데 cb( ) 가 출력된 뒤에

    console.log("waited 2 sec") 가 출력 되어야 되는거 아닌가요?


    function foo() { console.log('ahhh') }

    function bar() { foo(); }

    function start() { bar(); }

    start();


    이런경우는 

    foo

    bar

    start

    이런식으로 쌓여서 foo( ) 가 먼저 출력 되잖아요...

    console.log도 함수 아닌가요?




  • 메이플비
    452
    2021-07-04 11:35:44 작성 2021-07-04 11:55:55 수정됨

    지금 동기 비동기 방식을 전혀 다르게 이해하고 계신것 같습니다.

    동기의 경우 실행시에 바로 읽혀서 차례로 실행되는것이고,

    비동기의 경우 상황이 맞을때 까지 이벤트 루프에서 대기를 타는건데,

    이때 중요한건 비동기 안으로 들어가서 모든 코드를 읽고난 후 각각의 라인들이 따로따로 대기를 타는게 아니라, setTimeout 블록 자체가 대기를 타게 됩니다. 왜냐하면 주어진 시간이 오기 전까진 셋타임아웃에 넣은 함수가 호출(실행)이 되지 않으니까요. 

    그래서 2초의 시간이 지나면 setTimeout에 넣은 함수를 호출해서 그제서야 셋타임아웃 안에 있는 코드들을 위에서부터 라인별로 읽어나가게 되는거죠.

    (이 경우 콘솔로그, 그다음이 유저 아이디, 그리고 마지막으로 콜백함수 -> 콜백함수가 유저를 출력하는거니까 마지막으로 유저를 출력)

    그리고 댓글로 주신 예시는 전부 동기코드이기 때문에 함수가 호출되는 순서대로 실행이 되어야 되는게 맞구요,

    다만 두번째도 foo()부터 실행되는건 아니고,

    스타트부터 시작해서 호출한 함수가 끝나기를 대기를 탑니다.

    즉 실행 순서가 start -> bar -> foo -> console.log가 맞습니다.

    단지 start와 bar함수는 다른 함수 호출 이외에 아무것도 하는게 없으니까 디스플레이상 아무것도 안 보이는거구요.


  • 메이플비
    452
    2021-07-04 11:49:04 작성 2021-07-04 11:56:38 수정됨
    function findUserCallBack(id,cb){
        setTimeout(function(){ // 2 호출, 근데 비동기라 일단 대기 (인자: 콜백, 시간)
            console.log("wait 2 sec") // 4 콘솔로그
            const user = { // 5 유저 오브젝트값 선언 그리고 값 주기
                id:id
            };
            cb(user); // 6 콜백 실행 (유저를 인자로 넣어줌)
        },2000) // 3 라인이 다 읽혔지만 따로 코드는 없어서 2초 대기
    }
    findUserCallBack(1,function(user){ // 1 함수 호출 (인자는 1과, 콜백함수)
        console.log(user) // 7 콘솔로그 유저
    })
  • 공군소령김도지
    624
    2021-07-05 06:31:53

    아주 좋은 설명 감사합니다. 

    앙 설명띠

  • 로그인을 하시면 답변을 등록할 수 있습니다.