我正在使用$(文档)中的图形可视化库。准备好了()
,它似乎阻止了用户界面。我期望$(文档).就绪()
在一个单独的线程中执行。
有人知道细节吗?也许我应该使用setTimeout
异步运行代码?
编辑:
术语“异步”和“独立线程”在JavaScript中具有误导性。我不是JavaScript专家,也找不到更准确的术语。请参阅答案以进行澄清。
除了Web Worker(不能直接访问DOM)之外,JavaScript在一个线程中运行。$(文档)。准备好了()
是异步的,因为你传递给它的回调可能会立即触发,或者稍后某个时候DOM已经加载,但是当它的回调实际运行时,它是JS运行时唯一正在处理的事情。
例如,一个长时间运行的for
循环将阻塞UI线程,而不管它是否在就绪回调、事件处理程序、异步XHR的成功回调等中。防止它阻塞线程的唯一方法是通过使用setTimeout
调度稍后的块,将其拆分为多个循环。
在JavaScript中没有单独的线程。所有JavaScript都在一个线程中执行;UI更新在同一个线程中执行。如果你的JavaScript很忙,UI更新就不会发生。“异步”在这里有点误导;它意味着函数的执行将被推迟(但仍然在一个线程中)。
基本上,浏览器有一个执行队列。所有事件处理程序在触发时都放入执行队列;超时函数也是如此。当任何没有调用方的函数退出时,将执行队列中的下一个函数。UI更新也在同一队列中。
因此,当您执行$(文档)时。ready(fn)
,它(简单地说,jQuery使它有点复杂,尤其是对于较旧的浏览器)将附加一个处理程序。当加载所有内容时,浏览器将触发此处理程序,从而将其放入执行队列。轮到它时,它会执行;它执行的任何UI更新都将在处理程序退出时绘制。
--
*)单线程规则有一个例外:web工作者。每个web工作者都在自己的线程中运行,但他们的能力非常有限;基本上,只进行计算(他们根本无法访问UI)。
异步并不意味着多线程。Javascript是基于事件的,当发生某些事情(事件发生)时将调用函数。事件侦听函数不是在单独的线程上执行的,它只是计划稍后执行。
但是有一种古老的技术可以在不支持浏览器的情况下模拟多线程。它将一个完整的任务分解为延迟的小任务。
例如,您编写了以下内容,这将导致阻塞:
for (var i = 0; i < 10000000; i++) {
// do something very slow
}
将它转到这个位置,它将并行执行,几乎像多线程一样。
(function step(i) {
if (i < 10000000) {
// do something very slow
setTimeout(function() {step(i + 1)}, 1);
}
})(0);
编辑:我刚刚意识到这会导致内存问题,因为在每个步骤中,我们都引用了前一个步骤,使得GC无法清理内存。要克服这个问题,请将i
从参数中去掉,然后像这样更改它(我用一个函数包装整个过程,这样i
就不会在外部错误地更改):
(function() {
var i = 0;
(function step() {
if (i < 10000000) {
// do something very slow
i++;
setTimeout(step, 1);
}
})();
})();