当前位置: 首页 > news >正文

【每日一面】setTimeout 延时为 0 的情况

基础问答

问题:你在写代码的过程中,在什么时候才会设置 setTimeout 的延时为 0?

回答:有如下几种情况

  1. 避免同步任务阻塞 UI,即在渲染较多数据的时候,可以通过 setTimeout 分批渲染。
const data = new Array(1000).fill(1).map((x, idx) => idx + 1);function render(list) {let index = 0;for (; index < list.length; index += 100) {console.log('current', index);const current = index;setTimeout(() => { console.log(list.slice(current, current + 100).join(','))}, 0);}
}render(data);
  1. 获取 DOM 元素的宽高,本质是根据事件循环机制调整了代码的执行顺序。
function App() {const dom = document.querySelector('#app');console.log(dom.height);setTimeout(() => dom.height, 0);
}
  1. 代码分片,古早技术,将同步代码分片执行,避免阻塞渲染。

扩展延伸

JavaScript 单线程:JavaScript 是单线程语言,这个是编程语言的设计,在同一时间只能执行一段代码,所有的任务都需要排队,而身为单线程,但是好像我们访问网页的时候还是那么快,这语言优势这么强?这是另一个问题,语言设计上是单线程,只能同步的执行代码,但是浏览器不是,他是多线程的,分出来一个 JS 主线程用于执行 JavaScript 代码,还有如 UI 线程,用于执行渲染等。在 JavaScript 中,通过事件循环来协调任务执行,实现异步编程。

事件循环:这个机制是 JavaScript 的一个核心机制,可以利用这个机制实现高并发,异步编程操作。

核心是 - 调用栈、任务队列、宏任务、微任务

整个流程为 - JavaScript 代码按照代码依次执行时,检测到同步任务就进入调用栈执行,检测到宏任务,先压入宏任务队列,检测到微任务,则压入微任务队列,当本轮同步任务(宏任务)结束时,检测微任务队列,清空(即执行所有的微任务),这个检测的时机称为“微任务检查点”。

yuque_diagram (1)

如图,伴随着每个宏任务执行,都有自己对应的微任务队列,直到微任务队列全部执行完成,才会开启下一个宏任务。

setTimeout(callback, delayTime) API:在执行这个 API 时,JS 引擎会将 callback 函数封装成宏任务,挂载到延迟队列中,等待执行。这里再次引入了一个新的概念,延迟队列,这个是浏览器(或者引擎)实现的,当 JavaScript 创建定时器的时候,渲染进程就会将这个定时器的任务添加到延迟队列中。执行完一个任务,计算延迟队列中是否有到期的任务,有就执行,没有继续循环。

面试追问

  1. 延迟时间为 0,会立即执行吗?

不会,虽然我们设置为了 0,但是 setTimeout 的回调函数会被封装成一个宏任务,所以他需要等待同步任务执行结束后,从宏任务队列中取出来执行。此外,这个延迟时间虽然可以设置为 0,但是浏览器的最小执行时间实际是不一定的,Chrome 浏览器是 4ms。

  1. 那延迟时间设置为 400ms,会在 400ms 时执行吗?

不会,原因同上。setTimeout 只能做到“尽快执行”,而不是“立即执行”。

  1. 你在使用 setTimeout 的时候,有遇到过什么问题吗?

历史代码问题,存在比较多的 setTimeout 导致代码执行的结果不好理解。

this 指针问题,setTimeout 回调函数中的 this 和直觉不符,如果执行的回调函数是一个对象的方法,那么这个对象的方法中 this 并不是指向这个对象,而是全局。

长任务阻塞延迟的回调函数调用,如果当前任务执行的时间比较长,可能会导致回调函数等待。

浏览器优化问题,现在浏览器为了降低对电量的消耗,延长续航时间,会对后台界面的 setTimeout 执行时间间隔延长,一般会大于 1s,但是遇到过更久的,有一个多小时。

  1. 那有没有可以替代的 API?

有,和动画相关的可以使用 requestAnimationFrame API 来替代,可以保持和浏览器渲染频率一致,而不需要计算每帧的间隔时间来延迟执行。

微任务可以使用 Promise 来创建。

  1. 实现一个简单的 setTimeout。
/*** 用 requestAnimationFrame 实现简易 setTimeout* @param {number} delay - 延迟时间(毫秒)* @returns {number} - RAF的ID,用于取消(对应clearTimeout)*/​
function rafSetTimeout(callback, delay) {// 1. 记录延迟结束的目标时间(当前时间 + 延迟时间)const startTime = Date.now();const targetTime = startTime + delay;// 2. 定义递归执行的RAF回调函数function rafCallback() {// 3. 检查当前时间是否达到目标时间if (Date.now() >= targetTime) {// 达到目标时间,执行用户回调callback();} else {// 未达到,继续递归调用RAF,等待下一次重绘requestAnimationFrame(rafCallback);}}// 4. 启动第一次RAF,开始等待return requestAnimationFrame(rafCallback);
}/*** 对应 clearTimeout,取消 rafSetTimeout* @param {number} rafId - rafSetTimeout 返回的RAF ID*/
function rafClearTimeout(rafId) {cancelAnimationFrame(rafId);
}
  1. 经典题目,判断运行结果,这里给个简单的例子。
setTimeout(() => {console.log('回调1');
}, 0);// 插入同步任务
console.log('同步任务');setTimeout(() => {console.log('回调2');
}, 0);
http://www.hskmm.com/?act=detail&tid=20677

相关文章:

  • AI元人文:悟空博弈框架
  • sway - wayland下截图方案
  • 不同网络间文件互传怎么实现?
  • sway wayland下 wps-office无法输入中文
  • 科学史笔记
  • Spring XML 设置简介
  • 2025 年真空泵品牌最新权威推荐排行榜:覆盖真空泵维修,真空泵机组,真空泵油,真空泵配件领域选择指南
  • 专业的跨网文件交换系统 和传统FTP/U盘拷贝有什么区别?
  • 0voice-2.1.4-http服务器的实现
  • CF *2600 思维题 2
  • 中低压配网设备介绍
  • vue3使用路由配置
  • 分享一个YTB视频下载器
  • 基于Python+Vue开发的家具商城管理系统源码+运行步骤
  • 2025年,CRM口碑排行榜:从SAAS到本地部署方案
  • Arraylist集合原理分析
  • Commitlint 使用指南
  • VonaJS提供的读写分离,直观,优雅
  • 免费开源的百度文库平替!支持多格式文档预览与存储!
  • 个人行业选择
  • idea
  • 写假数据
  • C语言的指针与cpp的引用
  • day24——Java高级技术深度解析:单元测试、反射、注解与动态代理 - 指南
  • 进程互斥的硬件实现方式(比较难懂的一节课,但是我搞懂了)
  • 【征文计划】基于Rokid眼镜平台的AR历史情景课堂创意应用 - 指南
  • 忍了一年,我的SAAS CRM终于到期了!
  • Aduro电涌保护器特惠:6口插座与2USB端口52%折扣
  • centos 网卡IPv6配置
  • 免费发布网站html