JAVASCRIPT

javascript async,defer

funfunweb 2023. 11. 1. 10:51

 

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title></title>
  <script src="main.js"></script>
</head>
<body>
</body>
</html>

 

head안에 script 를 넣으면 사용자가 html 다운받았을때 브라우저가 분석해서 css와 병합하여 dom 요소로 변환하는데
스크립트 태그가 보이면 html parsing 하는것을 멈추고 main.js 다운받아서 실행하고 다시 parsing한다.

 

단점은 js 사이즈가 크고 내용이 많으면 느리게 뜬다.

(스크립트 다운받고 실행할때까지 스크립트 아래쪽 페이지를 볼수 없음)

 

스크립트에서는 스크립트 아래에 있는 dom요소에 접근할 수 없다.
스크립트를 head에 포함하는것은 좋지 않다.


그래서 많이 사용하는게 body 맨 끝에 넣는데

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title></title>

</head>
<body>
  <h2>console 출력</h2>

  
  <script src="main.js"></script>
</body>
</html>


html 모두 퍼싱후  모든 준비후 스크립트 다운 fetching하고 실행(executing)

 

단점 사용자가 기본적인 html 빨리 볼수있지만
javascript에 의존적인 페이지(자바스크립트로 이쁘게 꾸미는 웹사이트라면) 서버에서 자바스크립트를 fetching 하는 시간도 길어지게 되고 실행하는시간도 기다려야한다.

(html 문서 자체가 아주 클 경우 브라우저가 html 문서 전체를 다운로드 한 다음에 스크립트를 다운받게 되면 페이지가 정말 느려진다.)

 

해결법1

head안에 넣고 async 속성 사용 -페이지와 완전 독립적으로 동작

 

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title></title>
  <script src="main.js" async></script>
</head>
<body>
  <h2>console 출력</h2>

  
  
</body>
</html>

 

백그라운드에서 다운로드 되므로 html 페이지는 async스크립트 다운이 완료되길 기다리지 않고

페이지 내 콘텐츠를 처리, 출력(async 스크립트 실행중에는 html 파싱이 멈춘다.)

DOMContentLoaded 이벤트와 async 스크립트는 서로를 기다리지 않는다.

 

그러므로 페이지 구성이 끝난 후에 async 스크립트 다운로딩이 끝난 경우 DOMContentLoaded 는 async 스크립트 실행전에 발생할 수 있음.

async 스크립트가 짧아서 페이지 구성이 끝나기 전에 다운로드 되거나 스크립트가 캐싱처리 된 경우, DOMContentLoaded는 async 스크립트 실행 후에 발생할 수도 있다.

 

이런 특징 때문에 페이지에 async 스크립트가 여러 개 있는 경우,그 실행 순서가 제각각이 된다.

실행은 작성순서가 아닌  다운로드가 끝난 스크립트 순으로 진행되기 때문에  js 파일이 순서가 중요할 경우에는 문제가 발생할 수 있음.

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title></title>
  <script src="main1.js" async></script>
  <script src="main2.js" async></script>
</head>
<body>
  <h2>console 출력</h2>

  
  
</body>
</html>

 

결론 

브라우저가 html 다운로드 받아서  parsing 하다가 script 에  async 가 있으면 병렬로 msin.1.js 를 다운로드 받아라 라고 명령해놓고 다시  parsing하다가 main.js 가 다운로드 완료 되면  parsing을 멈추고 다운로드 된 js파일을 실행(executing)하게 된다.

실행을 한 후 나머지 html 을  parsing하게 된다.

장점 -

body 에 넣는것보다는 fetching 이  parsing 하는 동안 병렬적으로 일어나기 때문에 다운로드 받는 시간을 절약할수있다.

 

단점 -

1.js파일이 html  parsing되기 전에 실행되기 때문에 만약 js 파일에서 돔요소를 조작하게 되면 조작하려는 시점에 html 요소가 아직 정의 되기 전일 수 있다

2. html  parsing하는 동안에 언제든지 js 실행하기 위해서 멈출수 있어서 사용자가 페이지를 보는데 시간이 걸릴 수 있다.

 

 

해결법2

head안에 넣고 defer 속성 사용 (외부script 에서만 가능

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title></title>
  <script src="main1.js" defer></script>
  <script src="main2.js" defer></script>
  <script src="main3.js" defer></script>
</head>
<body>
  <h2>console 출력</h2>

  
  
</body>
</html>

 

브라우저가 html 다운로드 받아서  parsing 하다가 script 에 defer 속성이 있는 스크립트(defer스크립트 또는 지연스크립트)가 있으면 다운로드 명령만 하고 나머지 html 을 끝까지  parsing 하고 마지막에 다운로드 되어진 js 를 실행한다.

:: 브라우저는 defer 속성이 있는 스크립트를 백그라운드에서 다운로드 하기 때문에 다운로드 하는 동안 html parsing 은 멈추지 않는다.

스크립트 실행은 페이지 구성이 끝날때까지 지연된다.

html parsing 하는 동안 필요한 js파일을 모두 다운로드 받아놓고 html parsing 을 먼저해서 사용자에게 필요한 페이지를 먼저 보여주고 바로 이어서 js를 실행하므로 가장 좋다.

 

정리

 

defer스크립트는 페이지 생성을 절대 막지 않는다.

defer 스크립트는 DOM 이 준비된 후에 실행되긴 하지만  DOMContentLoaded 이벤트 발생 전에 실행됩니다.

 

순서 

HTML PARSING

MAIN1.JS

MAIN2.JS 

순으로 실행한다.

만약 MAIN1이 길이가 길고 MAIN2 길이가 짧은 스크립트라면

MAIN2는 다운로드가 먼저되지만 MAIN 1이 먼저 실행하고 MAIN2는 나중에 실행된다.

 

async와 defer 스크립트는 다운로드시 페이지 렌더링을 막지 않아서 사용자가 오래 기다리지 않고 페이지 컨텐츠를 볼 수 있게 할 수 있다.

 

실무에선 defer를 DOM 전체가 필요한 스크립트나 실행 순서가 중요한 경우에 적용한다.

 async방문자 수 카운터나 광고 관련 스크립트같이 독립적인 스크립트에 혹은 실행 순서가 중요하지 않은 경우에 적용한다.