defer vs DOMContentLoaded

  • defer이 먼저 실행된다

defer 여러개 실행 순서

1
2
<script src="./defer.js" defer></script>
<script src="./defer2.js" defer></script>
  • 여러개의 defer가 있으면 위에서 아래로 순서대로 실행된다
  • defer.js 실행 -> defer2.js 실행

async와 DOMContentLoaded는 서로 무관하다

  • DOMContentLoaded 먼저 실행될수도 async가 먼저 실행될수도 있다

참고

script태그 defer, async

  • 처음 웹 프로그래밍을 배울 때 스크립트 태그는 body 최하단에 위치시키라고 배웠다
  • 스크립트 로드 타이밍을 뒤로 미뤄서 돔을 조작하는 스크립트가 잘 동작한다
  • 근데 요즘에는 스크립트 태그를 head 안에 선언하는 경우가 많다
  • defer를 사용하면 위에서 말한 효과를 볼 수 있다
  • 근데 defer 말고도 async라는 속성을 줄 수 있다
  • 둘이 차이는 뭘까?

default (blocking further parsing)

  • 스크립트 태그를 만나면 파싱을 멈추고 스크립트를 로드하고 실행한다
  • 이 때 HTML 파싱을 멈춘다
  • 스크립트가 로드 다 끝나면 이어서 남은 파싱을 진행한다
  • 스크립트가 헤비 하면 화면이 안 그려지고 사용자가 떠날 확률도 높아지겠다

defer, async 공통

  • 스크립트는 스크립트대로 로드하고 HTML 파싱은 계속된다
  • 비동기적이다

defer

  • 스크립트 로드가 완료되었다고 바로 실행되지 않는다
  • HTML 파싱이 모두 끝나면 실행된다
  • 주로 돔을 조작하는 스크립트를 로드할 때 사용하면 된다

async

  • 스크립트가 로드되고 바로 실행된다
  • 돔을 조작하는 스크립트가 없을 때 사용하는 것이 좋다

참고

blog fix 카피 버튼이 안나오는 문제

  • 블로그에 코드 스니펫 오른쪽 위에 카피 버튼이 나올 때도 있고 안 나올 때도 있었다
  • 무한 새로고침하면 종종 모습을 비춘다… (이스터에그도 아닌 것이..)

환경

  • hexo-theme-icarus v4.0.1

해결하기

  • 일단 어디서 이 버튼을 생성하는지 알아내야 한다
  • 버튼이 정상적으로 출력되었을 때 해당 버튼의 클래스명을 확인했다
  • 그리고 main.js라는 곳에서 동적으로 버튼을 생성하고 있음을 알아냈다
themes\icarus\source\js\main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
console.log(clipboard, typeof ClipboardJS);
if (typeof ClipboardJS !== 'undefined' && clipboard) {
$('figure.highlight').each(function () {
const id = 'code-' + Date.now() + ((Math.random() * 1000) | 0);
const button =
'<a href="javascript:;" class="copy" title="Copy" data-clipboard-target="#' +
id +
' .code"><i class="fas fa-copy"></i></a>';
$(this).attr('id', id);
$(this).find('figcaption div.level-right').append(button);
});
new ClipboardJS('.highlight .copy'); // eslint-disable-line no-new
}
  • 조건문에 ClipboardJS가 있으면 버튼을 생성하는데
  • main.js에는 ClipboardJS에 대해 따로 선언이 없었고, 외부 라이브러리라는 것을 알았다
  • 이 ClipboardJS가 어디서 오는지 확인해야 했다
themes\icarus\layout\common\scripts.jsx
1
2
3
4
5
6
7
8
9
{
clipboard && (
<script
src={cdn('clipboard', '2.0.4', 'dist/clipboard.min.js')}
// async
defer
></script>
);
}
  • layout\common\scripts.jsx에서 가져오는 코드를 확인했는데
  • async으로 가져오고 있었다
  • 문제는 clipboardJS가 로드되기 전에 main.js가 먼저 실행되는 문제였다
  • defer로 수정하니까 잘 동작했다!

최신 버전 확인

  • hexo-theme-icarus v4.2.0
  • 최신 버전을 확인하니까 defer로 변경되어있었다… (이슈 검색했을 때는 못 찼았다…)
  • 처음부터 최신 버전을 참고하면 좋았겠다
  • 9개월 정도 나름대로 커스텀하면서 사용해왔는데,
  • 나중에 시간 나면 커스텀한 내용을 유지하면서 최신 버전에 맞게 업데이트해줘야겠다

참고

async, defer

setTimeout Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const delay = (ms) => {
return new Promise((resolve) =>
setTimeout(() => {
resolve(ms);
}, ms)
);
};
const main = async () => {
console.log(`main start`);

const result = delay(1000);
result.then(console.log);
console.log(`main end`);
};

const main2 = async () => {
console.log(`main start`);

const result = await delay(1000);
console.log(result);
console.log(`main end`);
};

main();
// main2();
  • promise를 알려주는 여러 글, 영상에서 setTimeout으로 예제로 진행한다
  • setTimeout을 promise로 감싸면 딜레이를 만들 수 있다
  • promise가 처음에 엄청 헷갈리는데, 막힐 때마다 이 예제를 먼저 작성해보고 적용해보았던 것 같다

참고

forEach await 안되는 문제

상황

  • 배열로 저장되어 있는 데이터를 forEach로 순회하면서 db에 INSERT하는 쿼리를 만들려고 했다
  • 근데 비동기적으로 동작했다
1
2
3
await data.forEach(async (element: ArticlePreview, idx: number) => {
// insert query
});

해결

1
2
3
4
5
6
7
8
9
for (const element of data) {
// ...
try {
const a = await databaseConnection.query(queryString, [
// ...
]);
}
// ...
}
  • for of에서는 await을 통해서 순차적으로 처리가 가능했다

시도해보지 않은 다른 방향

참고

Promise 연습, js 비동기 연습

js
1
2
3
4
// 프로미스 병렬처리 ; all
await Promise.all([delay2(1000), delay2(2000)]).then((result) => {
console.log(result.join(' + '));
});
  • 처음에는 익숙해지기 어렵지만, 꿀인 비동기

promise

  • js에는 promise라는게 있다
  • 비동기 흐름에서 중요하다
  • 프로미스는 선언과 동시에 실행이된다
  • 프로미스 결과를 처리하려면 then을 통해 받아 볼 수 있다

async await

js
1
2
3
4
5
6
7
8
async function delay2(ms) {
await new Promise((resolve) => {
setTimeout(() => {
resolve(ms);
}, ms);
});
return ms;
}
  • await 키워드는 async 함수 내에서만 사용가능하다
  • await 를 통해서 promise가 Fulfilled상태가 되야지만 다음줄의 코드가 실행된다

병렬처리

Promise.race

js
1
2
3
await Promise.race([delay2(1000), delay2(2000)]).then((result) => {
console.log(result);
});
  • 1초뒤에 1000만 반환하는 코드이다

Promise.all

js
1
2
3
await Promise.all([delay2(1000), delay2(2000)]).then((result) => {
console.log(result.join(' + '));
});
  • 2초뒤에 1000 + 2000을 반환하는 코드이다

요상한 문법

js
1
2
3
(async () => {
console.log(await delay2(1000));
})();