浏览器渲染过程中如何处理异步任务

本文涉及的产品
公网NAT网关,每月750个小时 15CU
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 在浏览器渲染过程中,异步任务通过事件循环机制处理。JS执行时,同步任务在主线程上执行,形成一个执行栈。异步任务则被推入任务队列中,待主线程空闲时按顺序调用,确保页面流畅渲染与响应。
  1. 浏览器渲染基础与异步任务的引入

    • 浏览器渲染流程概述:浏览器渲染主要包括解析HTML构建DOM树、解析CSS构建CSSOM(CSS对象模型)、将DOM树和CSSOM合并生成渲染树、布局(计算元素位置和大小)和绘制(将元素渲染到屏幕)等阶段。在这个过程中,JavaScript代码的执行会对渲染产生影响。
    • 异步任务的必要性:当JavaScript执行一些耗时的操作,如网络请求(fetch或XMLHttpRequest)、定时器(setTimeout、setInterval)等,如果这些操作是同步执行的,会阻塞浏览器的渲染,导致页面出现长时间的空白或无响应。因此,异步任务被引入,使得浏览器在执行这些耗时操作时可以继续进行其他渲染工作。
  2. 不同类型异步任务在渲染过程中的处理方式

    • 定时器(setTimeout/setInterval)
      • 任务排队机制:当执行setTimeoutsetInterval函数时,浏览器会将其回调函数注册为一个宏任务,并在指定的延迟时间后将该宏任务添加到宏任务队列中。例如,在页面加载时执行以下代码:
        console.log('Script start');
        setTimeout(() => {
                 
        console.log('Timeout callback');
        }, 1000);
        console.log('Script end');
        
      • 浏览器首先会执行同步代码,打印Script start,然后遇到setTimeout,将其回调函数注册为宏任务并等待1秒。接着继续执行同步代码打印Script end。1秒后,如果此时浏览器的调用栈为空且微任务队列也为空,就会执行setTimeout的回调函数,打印Timeout callback
      • 对渲染的影响:在定时器等待期间,浏览器可以正常进行渲染工作。但如果定时器回调函数中有修改DOM结构或样式的操作,会触发重新布局和重新绘制。例如,如果定时器回调函数中改变了一个元素的大小,浏览器会在执行该回调函数时重新计算布局并重新绘制页面。
    • 网络请求(Ajax、fetch)
      • 异步请求与回调处理:当使用fetchXMLHttpRequest进行网络请求时,这些请求会在后台异步进行。浏览器会发送请求,然后继续执行其他代码。当请求完成并收到响应后,对应的回调函数(对于fetch,是.then方法中的函数;对于XMLHttpRequest,是onreadystatechangeonload等事件处理函数)会被添加到宏任务队列中。例如:
        console.log('Request start');
        fetch('https://example.com/api/data')
        .then(response => response.json())
        .then(data => {
                 
         console.log('Data received:', data);
         // 在这里可能会更新DOM,触发渲染
        });
        console.log('Request sent');
        
      • 浏览器首先打印Request start,然后发送请求并继续打印Request sent。当数据返回后,处理数据的回调函数会被添加到宏任务队列,等待执行。如果在回调函数中有更新DOM的操作,如将获取的数据渲染到页面上的列表中,会触发DOM更新,导致浏览器重新渲染部分或全部页面。
      • 优化渲染性能:为了避免在数据返回后一次性更新大量DOM元素导致的性能问题,可以采用虚拟列表技术或者分批次更新DOM。例如,对于一个长列表数据的渲染,可以只渲染用户当前可视区域内的列表项,当用户滚动时再动态加载和渲染其他项。
    • Promise与微任务(Promise.then、MutationObserver)
      • 微任务的执行顺序:Promise的.then方法中的回调函数会被添加到微任务队列中。在当前宏任务(如包含Promise的脚本块)执行完毕后,浏览器会先清空微任务队列,再处理下一个宏任务。例如:
        console.log('Script start');
        const promise = Promise.resolve('Data');
        promise.then(result => {
                 
        console.log('Promise resolved:', result);
        });
        console.log('Script end');
        
      • 浏览器先打印Script start,然后将Promise.then回调函数添加到微任务队列。接着打印Script end,在当前宏任务结束后,浏览器会清空微任务队列,执行Promise.then回调函数,打印Promise resolved: Data
      • 与渲染的协同作用:微任务可以用于在合适的时机更新DOM,使得DOM更新与浏览器的渲染过程更加紧密地协同。例如,在一个复杂的应用中,当一个组件的状态发生变化导致需要更新DOM时,可以使用Promise来管理这个更新过程,将更新DOM的操作放在.then回调函数中,确保在当前宏任务完成后及时更新DOM,并且在更新DOM之前浏览器有机会完成之前的渲染工作,避免不必要的重绘和重流。
  3. 浏览器如何协调异步任务与渲染线程之间的关系

    • 利用事件循环机制:浏览器通过事件循环来协调JavaScript代码的执行和渲染工作。事件循环不断检查调用栈是否为空,如果为空,则从宏任务队列中取出一个任务(如果有)放到调用栈中执行。在每个宏任务执行完后,会清空微任务队列。这样,异步任务(宏任务和微任务)能够在合适的时机执行,而不会阻塞渲染线程。
    • 渲染时机与异步任务调度:浏览器会在JavaScript执行的间隙寻找合适的时机进行渲染。例如,在执行完一个宏任务和微任务队列后,如果没有新的宏任务立即需要执行,浏览器可能会进行一次渲染更新。另外,一些浏览器API(如requestAnimationFrame)可以让开发者将代码插入到浏览器的下一次重绘之前执行,这可以用于优化动画等需要与渲染紧密配合的场景。例如:
      function animate() {
             
      // 进行动画相关的DOM操作
      requestAnimationFrame(animate);
      }
      animate();
      
    • 在这个例子中,requestAnimationFrame函数将animate函数注册为一个在下一次重绘之前执行的任务。这样,animate函数中的DOM操作能够与浏览器的渲染过程同步,保证动画的流畅性。同时,在animate函数内部,可以合理地安排异步任务,如在动画的某个阶段进行网络请求或者更新数据,并且通过事件循环机制,这些异步任务不会干扰动画的正常执行和浏览器的渲染。
相关文章
|
25天前
|
JSON 移动开发 JavaScript
在浏览器执行js脚本的两种方式
【10月更文挑战第20天】本文介绍了在浏览器中执行HTTP请求的两种方式:`fetch`和`XMLHttpRequest`。`fetch`支持GET和POST请求,返回Promise对象,可以方便地处理异步操作。`XMLHttpRequest`则通过回调函数处理请求结果,适用于需要兼容旧浏览器的场景。文中还提供了具体的代码示例。
在浏览器执行js脚本的两种方式
|
6天前
|
前端开发 JavaScript
宏任务和微任务在浏览器渲染过程中的执行顺序
宏任务和微任务是浏览器事件循环中的两种任务类型。宏任务包括整体代码块、setTimeout等,微任务有Promise.then、MutationObserver等。每个宏任务执行完毕后,会先执行完所有微任务,再进行下一轮渲染或执行下一个宏任务。
|
7月前
|
前端开发
如何处理前端应用程序中的异步操作
前端异步操作常见方法包括回调函数、Promise 和 async/await。回调函数可能导致回调地狱,Promise 提供了更好的错误处理和链式调用,而 async/await 则基于 Promise,以同步风格处理异步任务,提高代码可读性。根据需求和喜好选择相应方法,以实现更可靠、易维护的代码。
|
7月前
|
缓存 小程序 前端开发
如何解决小程序异步请求问题
如何解决小程序异步请求问题
191 0
|
缓存 JavaScript 前端开发
如何优化代码性能、如何处理异步请求或者如何实现动态效果
如何优化代码性能、如何处理异步请求或者如何实现动态效果
68 0
|
缓存 移动开发 编解码
和我一起来看看浏览器的异步实现到底有哪些方式
前端为何会有异步这一说法,是因为前端的代码是在浏览器中运行的,而浏览器是单线程的,前端的代码是在主线程中运行的,如果有耗时的操作,就会阻塞主线程,导致页面卡顿,所以就有了异步这一说法。
316 0
|
存储 Web App开发 缓存
浏览器工作原理总结-页面渲染篇
在前段时间的面试中,许多候选人对于浏览器中代码的执行过程都不甚了解,多数是只停留在知道个大概。最近正好在极客时间上拜读了大神的浏览器工作原理,读完对浏览器工作原理有种豁然开朗之感,趁有空余时间就把自己对这块的理解整理了一下,希望对各位小伙伴能有所帮助吧。
|
Web App开发
浏览器事件机制中事件触发三个阶段?
js中时间执行的整个过程称之为事件流,分为三个阶段:事件捕获阶段,事件目标处理函数、事件冒泡。 当某歌元素触发某个事件(如:click),顶级对象document发出一个事件流,顺着dom的树节点向触发它的目标节点流去,直到达到目标元素,这个层层递进,向下找目标的过程为事件的捕获阶段,此过程与事件相应的函数是不会触发的。
3317 0
|
JavaScript 前端开发 网络协议
|
Web App开发 JavaScript 前端开发
浏览器的渲染机制(一)上
浏览器的渲染机制(一)上
228 0