![]() |
1
719
这是因为你在做合作性的多任务工作。 一个浏览器必须同时做很多事情,其中之一就是执行javascript。但javascript经常使用的一个功能是要求浏览器构建一个显示元素。这通常被认为是同步执行的(尤其是当javascript不是并行执行时),但不能保证是这样,而且javascript没有定义好的等待机制。
解决方案是“暂停”JavaScript的执行,让渲染线程跟上进度。这就是
(实际上,
IE6恰好更容易出现这种错误,但我已经看到它出现在老版本的Mozilla和Firefox中。 见菲利普罗伯茨谈话 "What the heck is the event loop?" 更详细的解释。 |
![]() |
2
592
前言: 重要提示:虽然它的投票率和接受率最高,但@staticsan接受的答案实际上是 不正确! -请参阅David Mulder的评论了解原因。 其他一些答案是正确的,但实际上并没有说明要解决的问题是什么,所以我创建了这个答案来展示详细的说明。
因此,我将发布
详细介绍浏览器的功能和使用方法
更新: 我做了一个jsiddle来演示下面的解释: http://jsfiddle.net/C2YBE/31/ . 许多 谢谢 感谢@thangchang帮助启动它。 更新2: 为了防止JSfiddle网站死机或删除代码,我在最后将代码添加到这个答案中。 细节 : 想象一下一个带有“做点什么”按钮和“结果”部分的Web应用程序。
这个
现在,你的用户开始测试这个,点击“做点什么”按钮,页面在那里停留了3分钟,他们变得焦躁不安,再次点击按钮,等待1分钟,什么都没有发生,再次点击按钮… 问题很明显——你需要一个“状态”分区,它显示了正在发生的事情。让我们看看这是怎么回事。
因此,您添加一个“status”div(最初为空),并修改
而且,你很高兴地将这个应用程序交给用户重新测试。 他们回来时看起来很生气。并解释当他们点击按钮时, 状态DIV从未更新为“正在计算…”状态!!!! 你挠头,在StackOverflow(或阅读文档或谷歌)上四处打听,然后意识到问题: 浏览器将事件产生的所有“todo”任务(包括ui任务和javascript命令)放入 单队列 . 不幸的是,用新的“calculating…”值重新绘制“status”DIV是一个单独的TODO,它将转到队列的末尾! 下面是用户测试期间事件的细分,每个事件之后的队列内容:
因此,根本的问题是“status”DIV的重新绘制事件被放置在队列末尾的“execute line 2”事件之后,这需要3分钟,所以实际的重新绘制直到计算完成之后才发生。
拯救来了
因此,为了解决您的问题,您可以修改
那么,现在事件序列和队列是什么样子的?
万岁!在开始计算之前,status div刚刚更新为“calculating…”!!!! 下面是jfiddle中的示例代码,说明了这些示例: http://jsfiddle.net/c2ybe/31/ : HTML代码:
javascript代码:(在执行
|
![]() |
3
85
看看约翰·雷西格的文章 How JavaScript Timers Work . 设置超时时,它实际上会将异步代码排队,直到引擎执行当前的调用堆栈。 |
![]() |
4
21
大多数浏览器都有一个称为主线程的进程,该进程负责执行一些JavaScript任务、UI更新,例如:绘制、重绘或回流等。 一些JavaScript执行和UI更新任务排队到浏览器消息队列,然后被发送到要执行的浏览器主线程。 当主线程繁忙时生成UI更新时,任务将添加到消息队列中。
|
![]() |
5
20
看看这个: setTimeout |
![]() |
6
18
这里有相互冲突的赞成的答案,没有证据就没有办法知道该相信谁。这里有证据表明@dvk是正确的,@salvadordali是错误的。后者声称:
4ms最小超时与正在发生的事情无关。实际发生的是,setTimeout将回调函数推送到执行队列的末尾。如果在setTimeout(callback,0)之后有运行几秒钟的阻塞代码,则在阻塞代码完成之前,将不会在几秒钟内执行回调。试试这个代码:
输出为:
|
![]() |
7
13
这样做的一个原因是将代码的执行延迟到单独的后续事件循环。当响应某种浏览器事件(例如鼠标单击)时,有时只需要执行操作
之后
处理当前事件。这个
编辑
现在是2015年,我应该注意到
|
![]() |
8
9
这是一个有着古老答案的老问题。我想重新审视这个问题,并回答为什么会发生这种情况,而不是为什么这种情况有用。 所以你有两个功能:
然后按以下顺序给他们打电话
这就是为什么:不可能
同样来自Mozilla:
阅读以下内容后获取P.S.信息 article . |
![]() |
9
8
因为它经过了一段时间
|
![]() |
10
3
另一件事是将函数调用推到堆栈的底部,防止递归调用函数时堆栈溢出。这有一个效果
|
![]() |
11
3
这两个评价最高的答案都是错误的。
Check out the MDN description on the concurrency model and the event loop
而且应该弄清楚发生了什么(MDN资源是一块真正的宝石)。和
简单使用
什么 事实上 这里所说的并不是“浏览器可能还没有完全准备好,因为并发性”,或者基于“每行都是一个添加到队列后面的事件”的内容。 这个 jsfiddle 由dvk提供确实说明了一个问题,但他的解释是不正确的。
他的代码中发生的事情是,他首先将事件处理程序附加到
然后,当您实际单击按钮时,
这就是它变得有趣的地方。我们已经习惯了把javascript看作是异步的,以至于我们很容易忽略这个小事实: 在执行下一帧之前,必须完全执行任何帧。 . 没有并发性,伙计们。 这是什么意思?这意味着每当从消息队列调用函数时,它都会阻塞队列,直到它生成的堆栈被清空为止。或者,更一般地说,它会一直阻塞,直到函数返回。IT块 一切 包括DOM呈现操作、滚动和其他操作。如果需要确认,只需尝试在小提琴中增加长时间运行操作的持续时间(例如,再运行外部循环10次),您会注意到,当它运行时,您无法滚动页面。如果运行时间足够长,浏览器会询问您是否要终止进程,因为这会使页面没有响应。正在执行帧,事件循环和消息队列将一直保持到完成为止。
那么,为什么这篇文章的副作用没有更新呢?因为当你
有
更改了dom中元素的值,您可以
这是因为我们实际上在等待代码完成运行。我们没有说“有人获取这个,然后用结果调用这个函数,谢谢,现在我已经完成了imma返回,现在做任何事情”,就像我们通常对基于事件的异步javascript所做的那样。我们输入一个click事件处理函数,更新一个dom元素,调用另一个函数,另一个函数工作很长时间,然后返回,然后更新同一个dom元素,以及 然后 我们从初始函数返回,有效地清空了堆栈。和 然后 浏览器可以访问队列中的下一条消息,这很可能是我们通过触发一些内部“on dom mutation”类型事件生成的消息。 在当前正在执行的框架完成(函数已返回)之前,浏览器用户界面无法(或选择不)更新用户界面。就个人而言,我认为这是设计而非限制。
为什么
注意a)长期运行功能
静止块
当它运行时,一切都会发生;b)您不能保证UI更新实际上在消息队列中处于领先地位。在2018年6月的Chrome浏览器上,
好吧,那么使用的问题是什么?
首先,使用问题
一位同事在对事件循环的错误理解中,试图通过使用一些模板呈现代码来“线程化”javascript。
第一个问题是显而易见的;您不能线程化JavaScript,所以在添加模糊时,您在这里什么也得不到。其次,您现在已经有效地将模板的呈现从可能的事件侦听器堆栈中分离出来,这些侦听器可能期望已经呈现了某个模板,但很可能还没有呈现。该函数的实际行为现在是非确定性的,正如任何运行它或依赖它的函数在不知情的情况下都是非确定性的。你可以做出有根据的猜测,但是你不能正确地为它的行为编码。
在编写依赖于其逻辑的新事件处理程序时,“修复”是
也
使用
但是什么 可以 我们来代替?好吧,正如引用的MDN文章所建议的那样,要么将工作拆分为多条消息(如果可以的话),以便排队的其他消息可以与您的工作交错并在运行时执行,要么使用Web工作者,它可以与您的页面一起运行,并在完成计算后返回结果。 哦,如果你在想,“好吧,难道我不能在长时间运行的函数中放一个回调,使其成为异步的吗?,那么不是。回调不会使它成为异步的,在显式调用回调之前,它仍然需要运行长时间运行的代码。 |
![]() |
12
2
关于执行循环和在其他代码完成之前呈现DOM的答案是正确的。javascript中的零秒超时有助于使代码伪多线程,即使它不是。 我想补充一下,在javascript中,跨浏览器/跨平台零秒超时的最佳值实际上是20毫秒,而不是0(零),因为许多移动浏览器由于AMD芯片的时钟限制无法注册小于20毫秒的超时。 此外,不涉及DOM操作的长时间运行的进程现在应该发送给Web工作者,因为他们提供了真正的多线程执行javascript。 |
![]() |
13
1
通过调用setTimeout,您可以给页面时间来响应用户正在做的任何事情。这对于在页面加载期间运行的函数特别有用。 |
![]() |
14
1
其他一些设置超时很有用的情况: 您希望将长时间运行的循环或计算拆分为较小的组件,这样浏览器就不会显示为“冻结”或说“页面上的脚本正忙”。 您希望在单击时禁用表单提交按钮,但如果禁用onclick处理程序中的按钮,则不会提交表单。设置时间为零的技巧是,允许事件结束,表单开始提交,然后您的按钮可以被禁用。 |
![]() |
15
1
设置为0的settimout在设置延期承诺的模式中也非常有用,您希望立即返回:
|
![]() |
16
1
问题是您试图对一个不存在的元素执行一个javascript操作。元素尚未加载,并且
|
![]() |
17
0
Javascript是单线程应用程序,因此不允许同时运行函数,因此可以使用此事件循环。所以,setTimeout(fn,0)所做的就是将其导入到任务请求中,当调用堆栈为空时执行任务请求。我知道这个解释很无聊,所以我建议你看这段视频,这将帮助你如何在浏览器的引擎盖下工作。 查看此视频: https://www.youtube.com/watch?time_continue=392&v=8aGhZQkoFbQ |
![]() |
Ezz Deghedy · 如何获取数据和读取响应 1 年前 |
![]() |
StuP · 响应式Vue组件在数据更改时不更新 2 年前 |
![]() |
burr · 让jQuery选择器识别新添加的DOM元素[关闭] 3 年前 |
![]() |
J. Hu · “表单提交已取消,因为表单未连接”[重复] 7 年前 |
![]() |
pploypiti · 选择所有其他ID不是“this”的元素。id' 7 年前 |
![]() |
xiaolingxiao · 导航到页面后运行javascript 7 年前 |