前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >setTimeout的那些事

setTimeout的那些事

作者头像
IMWeb前端团队
发布于 2019-12-04 05:21:53
发布于 2019-12-04 05:21:53
1.6K00
代码可运行
举报
文章被收录于专栏:IMWeb前端团队IMWeb前端团队
运行总次数:0
代码可运行

本文作者:IMWeb coolriver 原文出处:IMWeb社区 未经同意,禁止转载 如果懂setTimeout,可以直接看第3节,前面两节也可以当段子看一下。 如果不是很懂setTimeout,看下1,2两节应该会有一些收获。

1 JavaScript运行环境

之前关于service worker介绍的文章中,这样描述了浏览器环境下Javascript环境:"每个页面的javascript运行主线程都是一个Boss"、"Boss很厉害,在页面上指点江山,呼风唤雨。但他有个局限:同一时刻只做一件事(单线程)"。

以上体现了Javascript在浏览器运行环境中的局限性,单线程。实际上,不仅是在浏览器环境中,在Nodejs环境中的javascript也是单线程的。在不使用其它新员工(webworker等)的情况下,JS是如何在单线程上处理复杂的操作和逻辑,以至于在用户看来可以同时响应不同的操作的呢?

我们还是以Boss来称呼javascript的主线程吧。Boss为了更多更快地处理用户的需求,会不停地接收任务来执行。为了进一步提交效率,他优先执行最紧急的任务(即刻要执行),如果你要和他说"等下(3秒后 / 如果有我点了按钮 / 如果收到了服务器的响应)帮我在控制台打一个log吧。",BOSS不会专门等着去执行你的需求,而是默默地把你这个"伪需求"记在一个"小本本"上,然后拍拍胸脯和你说:"我保证(I promise!)",接着继续做手头上的事,等BOSS手头上事情做完了,会从小本本上选择最早记录的没被执行的任务来执行。

BOSS能力和时间有限,能做的只有这么多了。他Promise会帮你做的任务肯定会做(只要他没有猝死。。),但时间上可能并不一定严格符合你的要求,毕竟小本本上可能不仅只有一条任务。

想严肃了解JavaScript运行环境的同学可以看一下《JavaScript运行机制详解:再谈Event Loop》

2 理解setTimeout

咳咳。。是时候严肃一下了,我们改一下以上的称呼方式:

  • JS主线程 => BOSS
  • 同步任务 => BOSS手头上正在做的任务
  • 异步任务(队列) => BOSS的小本本上的任务

setTimeout这个方法相信很多初学者都有过误解:让JS从现在开始,经过指定的时间后,执行相应的函数。

从方法名和大部分现象来看,很容易产生以上的误解。在我们理解了JS主线程的特点后,知道了它会优先完成同步任务,在同步任务执行过程中,不会执行其它任务。

实际上,setTimeout做的事情是:在指定delay时间后,将指定方法作为异步任务添加到异步任务队列中

所以,如果setTimeout的定时到了执行时间,JS主线程仍然还在执行同步任务,setTimeout所指定的方法并不会立刻执行。

更惨的是,即使JS主线程执行完了同步任务,也不一定会执行setTimeout指定的方法,因为异步任务队列中可能有更早加入的异步任务。

最惨的是,即使天时地利人和,到了定时的时间时,JS主线程空闲,异步任务队列中只有setTimeout执行的方法,这个方法的执行时间也并不是精确的delay时间(精确到毫秒),因为浏览器上的计时器精确度有限:(以下摘自《Javascript高级程序设计(第三版)》)

  • IE8及更早版本的计时器精度为15.625ms
  • IE9及更晚版本的计时器精度为4ms
  • Firefox和Safari的计时器精度大约为10ms
  • Chrome的计时器精度为4ms

纵使setTimeout有些不尽人意,但这些瑕疵在大部分情况下,用户无法感知出来。

很多时候,setTimeout真正意图不是用来满足强迫者的精确需求,而是一种态度,一种中华名族传承已久的谦让美德:"You can you up!"。贯彻了此精神的代码,会让整个JS运行环境和谐运行。

给setTimeout一句评价就是:"上善若水,水善利万物而不争。" -- 摘自《道德经》undefined

3 setTimeout应用例子

3.1 替换setInterval来实现重复定时

setTimeout有个哥哥:setInterval。他哥看起来叼叼的,可以循环地每隔一个delay就向异步任务队列中添加一个任务。实际上setInterval用起来真地顺滑吗?以下YY一段setTimeout表哥的对话:

setTimeout: 欧妮桑 setInterval:纳泥? setTimeout:我发现你可能有bug! setInterval:我愚蠢的弟弟啊。。肯定是你使用的方法不对! setTimeout:考虑到JS运行环境的特点,你的定时方法可能会连续执行,之间没有预期的间隔。 setInterval:Too young too simple! 你是说JS主线程的步同任务执行时间很长,并且异步队列中只有我在往其中添加任务,导致我在异步队列中重复添加的任务没有及时被执行,然后JS主线程空闲后,我添加的多个任务就会连续执行,是吗? setTimeout:其实我想说的是。。。 setInterval:机智的为兄早就料到了这一点,于是我在往异步队列中添加任务的时候,特意检测了队列中是否已经有了我之前添加的任务,如果有的话,为兄就不再重复添加。 setTimeout:你说的那个检测机制我知道,我想说的是,当JS主线程中正在执行你添加的任务,如果此时异步任务队列为空,你再向队列中添加异步任务时,JS主线程执行完你上次添加的任务,会立刻执行你这次添加的任务。 setInterval:。。。。这是没办法的啊,我只能检测队列中的任务,没法检测正在执行的任务。You can you up? setTimeout:请看下面代码:undefined

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
setTimeout(function() {
    doWhatYouWantTo();
    setTimeout(arguments.callee, 100)
}, 100);

setInterval: (捂眼。。。)好亮的代码!你赢了...

使用以上setTimeout链式调用的方式,可以保证在下一次定时器代码执行之前,至少要等待指定的时间间隔,避免连续的运行。

3.2 防止事件疯狂触发

除了点击这种单次事件,浏览器上有一些会疯狂触发的事件,例如onreaize事件。如果给这个事件绑定了处理函数,在浏览器窗口大小改变的时候会很高频地触发处理函数。如果处理函数中有DOM操作的话,对页面性能影响会很大,尤其是在IE浏览器中,甚至可能让浏览器崩溃。

如果你实在需要在这类事件上绑定操作DOM的函数,那么可以考虑一下限制一下事件执行的时间间隔,至少不要那么频繁。至于设置多少时间间隔,看具体场景和需求。以下代码是利用setTimeout来实现事件执行频率控制:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 限制method执行频次,当方法100ms之内没有
 * 再次被调用时,才执行method方法
 * @param  {function} method  被限制的方法
 * @param  {Object} context method执行的上下文
 */
function throttle(method, context) {
    clearTimeout(method.tid);

    method.tid = setTimeout(function() {
        method.call(context);
    }, 100);
}

function fnResize() {
    console.log(111);
}

window.onresize = function() {
    throttle(fnResize);
}
3.3 IE下重新播放单次gif动画

有这样一个需求:设计给了一个gif动画,gif本身是单次播放的。产品要求页面加载时动画播放一次。后续用户只要鼠标hover到动画上,动画就重新播放一次。利用搜索引擎的强大功能,很快找到了实现方案:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$logo.on('mouseenter', function() { // hover时重新播放gif动画
    var $logoImg = $(this).find('img');
    $logoImg.attr('src', ''); // 将img的src置为空
    $logoImg.attr('src', _opt.logoImg); // 重新设置src为gif链接,以实现重新播放
});

在chrome等浏览器上验证没问题后,按照惯例,在IE上出问题了。。。gif并没有重新播放一次。

当时想的是,可能是IE反应太慢了,在src属性重置的那个间隔内,没有意识到这一点。于是就尝试加了个setTimeout,把重新设置src的操作丢到了异步任务队列中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$logo.on('mouseenter', function() { // hover时重新播放gif动画
    var $logoImg = $(this).find('img');
    $logoImg.attr('src', '');
    setTimeout(function() { // IE下要这样搞,不然不能重新播放动画
        $logoImg.attr('src', _opt.logoImg);
    }, 0);
});

虽然没有从根本上理解为什么IE会这样,但是setTimeout已然解决了这个问题。。

3.4 blur事件延时生效

经常有这种场景:监控input或者textarea中文本的变化,然后触发某个事件处理程序。考虑到除了键盘输入,还有鼠标的粘贴和剪切操作,比较完整的监控输入内容改变的方法是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 响应键盘输入,粘贴和剪切事件
$('#input').on('keyup paste cut', function() {
    console.log($(this).val());
});

以上代码在键盘输入场景下,能够在控制台输入最新的输入框内文本。但是当使用鼠标右键操作进行粘贴或剪切时,控制台输入的文本内容是操作前的旧内容。为了获取操作后的新文本内容,可以将对文本的获取和处理放在setTimeout中延时执行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 响应键盘输入,粘贴和剪切事件
$('#input').on('keyup paste cut', function() {
    var $this = $(this);
    setTimeout(function(){ // 使鼠标粘贴和剪切时,输入框内内容为最新
        console.log($this.val());
    }, 0)
});
3.5 更多用法?

setTimeout能够影响代码的执行顺序和时机,合理使用能够让更重要的代码优先执行,也可以FIX某些场景下的奇怪的bug。上面只列举了4种应用的场景,更多的用法欢迎大家讨论和补充。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-01-30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JavaScript 面试要点: Event Loop (事件循环)
单线程意味着,JavaScript 在执行代码的任何时候,都只有一个主线程来处理所有的任务。非阻塞则是当代码需要进行一项异步任务时,主线程会挂起这个任务,然后在异步任务返回结果时再根据一定规则去执行相应回调。
Cellinlab
2023/05/17
7220
JavaScript 面试要点: Event Loop (事件循环)
setTimeout(f,0)的作用及使用场景
setTimeout(f,0)的作用很简单,就是为了把f放到运行队列的最后去执行。 就是说,无论setTimeout(f,0)写在哪,都可以保证在队列的最后执行,因为它是异步操作。 js主线程会优先完成同步任务,在同步任务执行过程中,不会执行其它任务,setTimeout的定时到了执行时间,JS主线程仍然还在执行同步任务,setTimeout所指定的方法并不会立刻执行,当js主线程空闲,异步任务队列中只有setTimeout执行的方法时,才会继续执行setTimeout里的function。
薛定喵君
2019/11/11
1.5K0
教你做一些动图,学习一下 EventLoop
https://juejin.cn/post/6969028296893792286
程序员小猿
2021/07/06
4460
js中settimeout()的用法详解_低噪放工作原理
setTimeout与setInterval是JavaScript引擎提供的两个定时器方法,分别用于函数的延时执行和循环调用。前者的主要思想是通过一个定时器,让函数在计时结束后再执行;后者则是每隔一定的时间,就启动一次函数的执行。
全栈程序员站长
2022/11/09
1.8K0
js中settimeout()的用法详解_低噪放工作原理
【THE LAST TIME】彻底吃透 JavaScript 执行机制
首先我们需要声明下,JavaScript 的执行和运行是两个不同概念的,执行,一般依赖于环境,比如 node、浏览器、Ringo 等, JavaScript 在不同环境下的执行机制可能并不相同。而今天我们要讨论的 Event Loop 就是 JavaScript 的一种执行方式。所以下文我们还会梳理 node 的执行方式。而运行呢,是指JavaScript 的解析引擎。这是统一的。
Nealyang
2019/09/29
4730
【THE LAST TIME】彻底吃透 JavaScript 执行机制
小白理解 JavaScript 执行机制
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
前端魔法师
2021/01/27
6250
每天10个前端小知识 【Day 10】
在es5中主要是通过构造函数方式和原型方式来定义一个类,在es6中我们可以通过class来定义类。
程序媛夏天
2024/01/18
1610
每天10个前端小知识 【Day 10】
JavaScript运行机制
这一题看似很简单,但如果你不了解JavaScript运行机制,很容易就答错了。题目的答案是依次输出1 2 3,如果你有疑惑,下文有详细解释。
一觉睡到小时候
2019/07/03
7590
JavaScript运行机制
JavaScript——事件循环机制
JavaScript的任务分为两种同步和异步,它们的处理方式也各自不同,同步任务是直接放在主线程上排队依次执行,异步任务会放在任务队列中,若有多个异步任务则需要在任务队列中排队等待,任务队列类似于缓冲区,任务下一步会被移到调用栈然后主线程执行调用栈的任务。
思索
2024/08/16
1510
JavaScript——事件循环机制
揭开 JavaScript 事件循环的神秘面纱
Javascript 是一种单线程语言,这意味着它一次只能执行一个任务。但是,它仍然设法同时执行多项任务。它通过使用一些复杂的数据结构给人一种多线程的错觉。为实现这一点,Javascript 引擎有一个称为事件循环的重要组件。我们将了解什么是事件循环以及它如何在不阻塞主线程的情况下处理异步任务。
IT千锋教育
2023/05/31
3100
你不知道的Javascript:有趣的setTimeout
今天在回顾JavaScript进阶用法的时候,发现一个有趣的问题,话不多说,先上代码:
司想君
2018/02/02
7570
你不知道的Javascript:有趣的setTimeout
深入理解Javascript单线程谈Event Loop
假如面试回答js的运行机制时,你可能说出这么一段话:“Javascript的事件分同步任务和异步任务,遇到同步任务就放在执行栈中执行,而碰到异步任务就放到任务队列之中,等到执行栈执行完毕之后再去执行任务队列之中的事件。”但你能说出背后的原因吗?
小周sir
2019/09/23
1.6K0
深入理解Javascript单线程谈Event Loop
详解JavaScript 执行机制
而第二个例子则可能优点小问题,JavaScript 从上到下执行,那么遇到 0s 的计时器函数,就应该先输出 2 才对啊。这就是因为后面要提到的 JavaScript 执行机制导致的啦,因为 setTimeout 是异步任务。
赤蓝紫
2023/01/05
7200
详解JavaScript 执行机制
JS的运行机制
代码块: JS中的代码块是指由<script>标签分割的代码段。JS是按照代码块来进行编译和执行的,代码块间相互独立(即就算代码块1出错,但不影响代码块2的加载和执行),但变量和方法共享。
用户3159471
2020/07/21
2.5K0
JS 事件循环
众所周知,JavaScript 是一门单线程语言,虽然在 html5 中提出了 Web-Worker ,但这并未改变 JavaScript 是单线程这一核心,可是浏览器又能很好的处理异步请求,那么到底是为什么呢?
Umbrella1024
2021/03/25
15.9K0
JavaScript之Event Loop
先看段代码: console.log(1); setTimeout(function () { console.log(2); new Promise(function (resolve, reject) { console.log(3); resolve(); console.log(4); }).then(function () { console.log(5); }); }); function fn
laixiangran
2018/04/24
8030
JavaScript之Event Loop
深入理解JavaScript的Event-Loop机制
JavaScript 是单线程的,只有JS引擎线程执行事件队列的事件。为了防止代码阻塞,JavaScript使用了异步执行机制。
伯爵
2019/10/14
6440
Event Loop
一、为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。 JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准? 所
前朝楚水
2018/04/02
1.5K0
Event Loop
JavaScript执行机制
JavaScript为什么是单线程的呢?由于设计之初,JavaScript是用来做用户交互以及页面动态渲染,所以为了简洁和方便入手,决定了它只能是单线程,否则将会带来非常复杂的同步问题。
lealc
2024/03/19
4812
JavaScript执行机制
JavaScript 运行机制详解:再谈Event Loop
一年前,我写了一篇《什么是 Event Loop?》,谈了我对Event Loop的理解。 上个月,我偶然看到了Philip Roberts的演讲《Help, I'm stuck in an event-loop》。这才尴尬地发现,自己的理解是错的。我决定重写这个题目,详细、完整、正确地描述JavaScript引擎的内部运行机制。下面就是我的重写。 进入正文之前,插播一条消息。我的新书《ECMAScript 6入门》出版了(版权页,内页1,内页2),铜版纸全彩印刷,非常精美,还附有索引(当然价格也比同类书籍
ruanyf
2018/04/12
1.1K0
JavaScript 运行机制详解:再谈Event Loop
相关推荐
JavaScript 面试要点: Event Loop (事件循环)
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验