DOM 업데이트가 비동기적인 것 같을 때

한 페이지에 요소의 컨텐트를 업데이트하고 바로 로그아웃할 수 있는 JavaScript가 있습니다.
<span id="element">
  original text
</span>

<script>
  document.getElementById('element').innerHTML = "updated text";
  console.log(element.innerHTML);
</script>
페이지를 로드하면 [업데이트된 텍스트]가 화면에 표시될 것으로 올바르게 예상할 수 있습니다.그것이 console.log()을 통해 노출되었을 때, 너도 같은 값을 볼 수 있을 것이다.console.log()에서 이 결과가 나온 것은 이상하지 않다. 왜냐하면 DOM 업데이트는 동기화 이벤트이기 때문이다.DOM 객체의 속성을 수정할 때 이 변경 사항은 호출 스택에 던져지고 스택이 다시 비어 있을 때까지 진행 중인 이벤트를 실행할 수 없습니다.JavaScript의 이벤트 순환은 다음과 같습니다. 선진적인 선출입니다. 많은 이벤트가 브라우저 API로 처리된 비동기적인 작업(예를 들어 setTimeout())만 시작되었더라도.

같은 스레드;다른 발걸음

Despite it being synchronous, before a DOM change can be made visible on the screen, several processes 발생: 렌더링 트리 업데이트, 페이지 레이아웃 회류, 결과 최종 그리기.이러한 프로세스는 JavaScript의 이벤트 순환과 같은 스레드에서 실행되지만 속도는 다릅니다.내가 함께 놓은 이 졸렬한 예를 생각해 봐라.
이벤트 순환 및 다시 그리기 순환
가로줄은 브라우저의 메인 라인을 대표합니다. 웹 워크러스 (you probably should be) 를 이용하는 것과 같은 일을 하지 않는 한 모든 것이 실행됩니다.
작은 세로 막대는 JavaScript 이벤트 순환의 실행을 나타냅니다.이것은 실행 속도가 매우 빠르고 실행 호출 창고의 프로젝트와 같이 빠르며 보통 실행 과정에서 브라우저 API와 통신한다.
굵은 세로줄은 브라우저가 화면을 다시 그리는 빈도를 나타냅니다.이 비율은 보통 sits at around 60 frames/second, 또는 대략 16일에 한 번씩 발생한다.66밀리초.매번 발생할 때마다 현재 드로잉과 이전 드로잉 사이의 모든 DOM 변경 사항을 가져와 사용자에게 표시합니다.
대부분의 경우 이벤트 순환과 다시 그리기 주기 간의 관계는 우리의 업무에 중요하지 않고 실제적으로 사용자 체험과 무관하다.그러나 때때로 전체 DOM이 화면에 도착하는 과정이 DOM을 수정하는 것이 정말 동기화 작업인지 의심스러울 때 이런 상황이 발생한다.

alert()를 사용하여 DOM 업데이트를 비동기적으로 표시

Let’s explore one of these circumstances. To do so, I’m using a simple local Node server with a bit of HTML. You’re welcome to follow along with the changes we’ll be making by cloning the repo for yourself, 그리고 필요에 따라 주석의 모든 예시를 취소합니다.(이 예시에서 CodeSandbox 같은 것을 사용하려고 했지만, 가능한 한 순수하고 예측 가능한 환경이 필요합니다. 삽입식 iframe를 통해 사이트에 서비스를 제공하는 플랫폼은 이 점을 제공할 수 없습니다.)
이번에는 수정된 DOM 값을 로그아웃하지 않고 alert()으로 공개합니다.이 작업이 호출 창고에 던져질 때, 완료되지 않은 재그리기를 포함하여 주 라인을 동결합니다.
const element = document.getElementById('element');
element.innerHTML = "updated text";
alert(element.innerHTML);
페이지를 새로 고치면 "업데이트된 텍스트"가 경고 상자에 올바르게 표시됩니다. 이것은 DOM 업데이트 자체가 동기화되었음을 나타냅니다.그러나 실제로 화면에 렌더링된 내용을 보면 원하는 것이 아닐 수도 있습니다.

DOM 객체의 수정이 완료되었지만 적절한 재구성이 이루어지지 않았습니다(브라우저의 새로 고침 속도보다 이벤트 순환의 회전 속도가 훨씬 빠르다는 것을 기억하십시오.)alert()이 해제되는 순간, 다시 그리기가 허용되고, 우리의 텍스트가 업데이트됩니다.

일을 더욱 예측성 있게 하다

Again, this quirk is unlikely to trip-up any real-world work, but since we’re here anyway, let’s toy with making things behave a little more predictably. Say we wanted the alert() to fire only after the DOM change has been rendered to the screen. To do so, we have a couple of options, one of which is hackier than the other.

옵션 #1: 지연 경고 ().

By wrapping our alert() in a setTimeout() , we can wait long enough for any outstanding repaints to occur, and successfully fire it after the updated text has been rendered.

const element = document.getElementById('element');
element.innerHTML = "updated text";

setTimeout(() => {
    alert(element.innerHTML);
}, 20); // <-- arbitrary number greater than refresh rate

This “works,” but it isn’t the most elegant solution, mainly because we’re making an educated guess as to when the repaint cycle will have turned over. We don’t want to wait too long, causing an unnecessary delay for the alert() . But if we cut it close and the timeout fires sooner than that delay, we’re not guaranteed to see the correct text on the screen when it’s frozen, because the browser didn’t have a chance to paint outstanding DOM changes.

We can illustrate this by setting the timeout’s delay to something less than the refresh rate and reload the page a few times. Much of the time, the text will still correctly render. But sooner or later, the rendered text will not have updated like we want.

const element = document.getElementById('element');
element.innerHTML = "updated text";

setTimeout(() => {
    alert(element.innerHTML);
}, 5); // <-- risky, since the delay is less than the browser's refresh rate

Since we can’t undoubtedly predict the refresh rate of the browser (especially considering the range of things that could impact the main thread’s throughput overall), this option is pretty far under the bar of “ideal.” Thankfully, the browser offers a built-in API for navigating the repaint cycle by design, providing us with a much friendlier option.

옵션 2: 다음 번 페인트 다시 뿌린 후 점화

The browser’s requestAnimationFrame API lets us 은 우리로 하여금 우리의 목표에서 어떠한 추측 게임도 피할 수 있게 한다.우리의 목적을 달성하기 위해서, 우리는 그 중의 한 차례의 조정에서 우리의 alert()을 해고하기 쉬우며, 이를 좋은 것이라고 부른다.
const element = document.getElementById("element");
element.innerHTML = "updated text";

requestAnimationFrame(() => {
    alert(element.innerHTML);
});
근데 이건 안돼.다음 그림을 다시 그리기 전에 불을 켜는 것은 무의미하다.반대로, DOM 업데이트가 화면에 그려지기 때문에 나중에 발생하기를 바랍니다.이 점을 실현하기 위해서 우리는 단지 몇 가지 물건을 끼워 넣을 필요가 있다.
const element = document.getElementById('element');
element.innerHTML = "updated text";

requestAnimationFrame(() => {
    // fires before next repaint

    requestAnimationFrame(() => {
        // fires before the _next_ next repaint
        // ...which is effectively _after_ the next repaint

        alert(element.innerHTML);
    });
});
그것이 있으면 우리는 출발할 수 있다.alert()은 완료되지 않은 DOM 변경 사항이 화면에 그려진 경우에만 표시됩니다.따라서 alert()에서 본 내용과 페이지에 나타난 내용은 항상 같습니다.
fire a callback before the browser’s next repaint

The Broader Implications

Experimenting like this is fun in & of itself, but it should also impact the way you think more broadly about the relationship between the DOM and the browser’s rendering engine, especially as it all impacts the performance of your code.

There’s more going than what you might think when a DOM attribute is modified, and when the black box of a browser starts to become a little more translucent, it can set you up to understand & tackle tangential problems that impact user experience more apparently, like how certain changes to the DOM trigger synchronous style & layout updates (Paul Irish has a good starter resource).
그러나 이런 길을 걷지 않더라도 브라우저의 내부 작업에 대해 예전보다 더 많이 생각하게 해주길 바란다. 다른 이유가 없다면 이 도구가 하는 모든 것을 더 잘 감상하기 위해서다.
_소리 질러!이 모든 것은 Dr. Axel Rauschmayer의 계발을 받았습니다. 그는 그 중에서 DOM 변경이 화면에 보이는 후에야 alert()을 안전하게 시작할 수 있습니다.이 문제와 다음 토론은 왜 최선의 해결 방안이 이런 식으로 작동하는지 철저히 이해하게 해 주었다_
(이것은macarthur.me.Read it online here에 발표된 글입니다.)

좋은 웹페이지 즐겨찾기