본문 바로가기
JavaScript/JavaScript

Javascript) 동기식 / 비동기식 - Timer API, DOM 관련 이벤트 처리, DOM 연속

by 박채니 2022. 6. 2.

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


동기 (synchronous) / 비동기 (asynchronous)

 

비동기

- Javascript는 싱글 쓰레드로 진행

- 비동기처리함수(Timer API, DOM 관련 이벤트 처리, Ajax 요청) 호출 시, 바로 처리되는 것이 아닌 백 그라운드에 처리 위임

- 동기함수의 모든 처리가 끝나면 (call stack이 비워지면), 실행

* heap : 객체 저장 공간

* call stack : 함수 실행 스택

 

* Web APIs

* Callback Queue

* Event Loop

 

 

실행 과정 눈으로 보기

http://latentflip.com/loupe/?code=CgoKCgokLm9uKCdidXR0b24nLCAnY2xpY2snLCBmdW5jdGlvbiBvbkNsaWNrKCkgewogICAgc2V0VGltZW91dChmdW5jdGlvbiB0aW1lcigpIHsKICAgICAgICBjb25zb2xlLmxvZygnWW91IGNsaWNrZWQgdGhlIGJ1dHRvbiEnKTsgICAgCiAgICB9LCAyMDAwKTsKfSk7Cgpjb25zb2xlLmxvZygiSGkhIik7CgpzZXRUaW1lb3V0KGZ1bmN0aW9uIHRpbWVvdXQoKSB7CiAgICBjb25zb2xlLmxvZygiQ2xpY2sgdGhlIGJ1dHRvbiEiKTsKfSwgNTAwMCk7Cgpjb25zb2xlLmxvZygiV2VsY29tZSB0byBsb3VwZS4iKTs%3D!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D


HTML 코드

<button id="btn1">Async - Timer</button>

 

Javascript 코드

동기식 처리

btn1.onclick = () => {
    // 동기식 처리
    let result = foo();
    console.log(result);
};

const foo = () => {
    console.log('foo');
    return 100;
};

동기식 처리는 흔히 아는 것처럼 위에서 아래로 순서에 따라 처리 됩니다.

result라는 공간이 생성되고 foo()함수가 호출 되면 foo()함수로 이동하여 함수를 실행 및 값을 리턴하였으며, 해당 값을 result에 담아내어 console.log로 호출하였습니다.

따라서 결과 값이 순서대로 'foo', '100'이 호출 되었습니다.

 

그렇다면 비동기처리 함수는 어떻게 다를까요?


비동기처리 함수 - Timer API

- Timer API 함수들은 호출과 동시에 background(Web APIs)에 처리를 위임

btn1.onclick = () => {
    // 비동기식 처리
    let result2;
    // Timer API 함수들은 호출과 동시에 background(Web APIs)에 처리를 위임
    setTimeout(() => {
        result2 = 100;
        console.log('result2@callback : ', result2);
    }, 1000);
    console.log('result2 = ', result2);
};

result2가 먼저 출력되고 'result@callback'이 1초 뒤에 출력되었습니다.

실행 시간을 0으로 변경해도 동일한 결과가 출력됩니다.

 

즉, setTimeout()의 실행을 기다리지 않은 것을 알 수 있습니다.

 

동기함수CALL STACK에서 호출되고 실행되면 사라집니다.

하지만 비동기처리함수WEB API에 처리를 위임했다가 지정한 시간(1000)이 지나면 Callback Queue로 넘어가게 되고,

EVENT LOOP에 의해 CALL STACK이 비워지면 Callback Queue에 있는 것을 CALL STACK으로 밀어넣고 실행됩니다.

 

따라서 setTimeout()의 'result2@callback'보다 'result2'가 먼저 실행되었던 것입니다.

 

 

원하는대로 result2에 100이 대입 되고 출력을 하고 싶다면

btn1.onclick = () => {
    // 비동기식 처리
    let result2;
    // Timer API 함수들은 호출과 동시에 background(Web APIs)에 처리를 위임
    setTimeout(() => {
        result2 = 100;
        console.log('result2@callback : ', result2);
        console.log('result2 = ', result2);
    }, 0);
};

코드를 모아서 처리해줍니다.


비동기처리 - DOM 관련, event 처리

HTML 코드

<button id="btn2">Async - DOM</button>

 

Javascript 코드

 

js 폴더를 만들고 1.js파일을 생성해보았습니다.

 

해당 파일을 불러와 html 코드의 script 태그로 지정하여 사용해보려고 합니다.

btn2.onclick = () => {
    loadScript("../js/1.js");
    bar(); // Uncaught ReferenceError: bar is not defined
};

const loadScript = (path) => {
    const script = document.createElement('script');
    script.src = path;
    document.head.append(script); // 비동기처리함수
};

하지만 "Uncaught ReferenceError : bar is not defined" 와 같은 정의 되지 않은 함수라는 오류 메세지가 출력되는 것을 확인할 수 있습니다.

그 이유는 document.head.append(script)가 비동기처리함수이기 때문이죠.

 

실행 순서를 따라가보겠습니다.

 

loadScript()함수를 호출하면서 "../js/1.js" 경로를 매개인자로 넘겨주었습니다.

loadScript()함수가 실행되면서 'script'태그를 생성하고 script태그의 src 속성에 넘겨받은 경로(path)를 지정하였습니다.

그 후 document.head.append(script), head태그의 script태그를 append 추가하였는데, 해당 코드가 비동기처리함수이므로 실행을 기다리지 않고 바로 리턴 되었고, 실행을 기다리지 않은 상태에서 bar()를 호출하려고 하니 존재하지 않는 함수라는 메세지가 출력되는 것입니다.

 

실제로는 요소로 추가 되었고,

비동기처리함수는 callStack이 비워지면 실행되므로, 개발도구에서 bar()를 호출하면 잘 불러와집니다.

 

등록된 후 호출을 원할 시

btn2.onclick = () => {
    loadScript("../js/1.js", () => {
        console.log('script load 완료!');
        bar();
    });
};

const loadScript = (path, callback) => {
    const script = document.createElement('script');
    script.src = path;
    script.onload = callback;
    document.head.append(script);
};

callback함수를 넘겨주어 script가 load가 되면 실행하도록 해주었습니다.

 


DOM 연속

 

HTML 코드

<button id="btn3">Async - DOM 연속</button>

 

1.js 파일

// 1.js파일
const bar = () => {
    console.log('bar');
    return '../js/2.js';
};

 

2.js파일

// 2.js파일
const car = () => {
    console.log('car');
    return "js/3.js"
};

 

Javascript 코드

 

js/1.js 로드 - bar() 다음에 로드할 js의 주소 값 리턴

const loadScript = (path, callback) => {
    const script = document.createElement('script');
    script.src = path;
    script.onload = callback;
    document.head.append(script);
};

btn3.onclick = () => {
    loadScript('../js/1.js', () => {
        loadScript(bar(), () => {
            car();
        });
    });
};

bar()의 리턴 값이 다음에 로드할 js의 주소 값이기 때문에 path로 넘겨주고, callback함수에서 car() 함수를 호출하였습니다.

 

 

@실습 - 배경색 연속 변경

- .bg-box 배경색을 1초 단위로 빨 - 초 - 노 - 파 - 핑 변경 처리

 

HTML 코드

<button id="btn4">@실습문제 - 배경색 연속변경</button>
<div class="bg-box"></div>

 

CSS 코드

<style>
.bg-box {
    width: 100px;
    height: 100px;
    margin: 10px;
    border: 1px solid black;
}
</style>

 

Javascript 코드

btn4.onclick = () => {
    const target = document.querySelector(".bg-box");

    setTimeout(() => {
        target.style.backgroundColor = 'red';
        setTimeout(() => {
            target.style.backgroundColor = 'green';
            setTimeout(() => {
                target.style.backgroundColor = 'yellow';
                setTimeout(() => {
                    target.style.backgroundColor = 'blue';
                    setTimeout(() => {
                        target.style.backgroundColor = 'pink';
                    }, 1000);
                }, 1000);
            },1000);
        },1000);
    },1000);
};