如何在Javascript中做一个不会阻塞UI的循环
How to do a loop in Javascript that doesn't block the UI?
我的页面上有一些javascript需要很长时间才能执行(在10-30秒之间)
代码基本上是这样的:
//Iterate over all the elements in the array
for(int i=0; i<array.length; i++){
//Complex stuff for each element
}
问题是,当这段代码正在执行时,UI没有响应。
有什么办法可以解决这个问题吗?我知道javascript有一些异步,但我从来没有真正使用过它…
同时,元素的处理顺序必须始终与它们在数组中出现的顺序一致。
EDIT:我尝试了setTimeout()和"副本"下面的解决方案,但它仍然不能解决我的问题。我猜这是因为我的数组的大小不是很大,但每个元素的计算是相当大的。
我想要响应的UI是一个动画加载gif。由于每个项目的计算太大,所以动画很草率。
创建一个变量,并将其设置为计数器的起始值。
创建一个函数:
- 执行循环体执行的任何操作
- 增加计数器
- 测试计数器是否仍在长度范围内,如果是,则再次调用函数
要在每次调用函数之间暂停,以便有时间触发其他函数,请将对该函数的直接调用替换为对setTimeout
的调用,并使用该函数作为第一个参数。
var counter = 0;
function iterator () {
//Complex stuff for each element
counter++;
if (counter < array.length) {
setTimeout(iterator, 5);
}
}
Javascript使用了一种叫做"事件循环"的东西。基本上,只有一个线程在每个事件发生时执行代码。
有一个名为setTimeout
的函数,您可以使用它来使Javascript代码在以后的循环迭代中运行。在长时间运行的Javascript任务期间,一些开发人员用来保持UI响应性的一个技巧是,在超时为零的情况下定期调用要运行的代码。这将导致代码几乎立即执行,而不会锁定事件循环。
例如,你可以写
var i = 0;
setTimeout(myFunction, 0);
function myFunction() {
// complex stuff for each element.
i++;
if (i < array.length)
setTimeout(myFunction, 0);
}
请注意,在现代浏览器上,只要Javascript代码正在执行,UI将是无响应的。有些浏览器甚至会弹出一条消息,敦促用户终止该脚本;非常糟糕!
另外,在现代浏览器中,有一个新功能叫做"Web Workers"。Web Workers允许Javascript代码在单独的线程中执行。你可以创建任意多的线程,所以如果你的任务很容易并行化,也可以考虑使用web worker。
如果你需要浏览器在你的JS代码中间更新页面,你需要屈服于浏览器,例如用一个setTimeout
,在一个延续传递样式:
function doStuff(i) {
// do some stuff with index i
if (i < array.length) {
setTimeout(doStuff.bind(window, i + 1), 0);
}
}
doStuff(0);
我倾向于这样做:
function doStuff(arr) {
if (arr.length === 0) {
// do whatever you need to do when you're done
return;
}
// Do body of computation here
// Or use setTimeout if your browser doesn't do setImmediate
setImmediate(function () { doStuff(arr.slice(1)); });
}
doStuff(actualArrayOfThings);
基本上,将循环体分解,并使用递归按顺序运行循环的每个部分。将其分解为辅助函数非常容易,因为循环体本身是变化的。比如:
function eachAsync(arr, body) {
if (arr.length === 0) {
// do whatever you need to do when you're done
return;
}
body(arr[0]);
setImmediate(function () { eachAsync(arr.slice(1), body); });
}
eachAsync(actualArrayOfThings, doStuff);
如果你的任务是在不干扰用户界面的情况下处理数据,你可以使用WebWorkershttps://developer.mozilla.org/en/docs/Web/Guide/Performance/Using_web_workers
注意现代浏览器都支持
如果for循环正在做一些整洁的工作,则使用setTimeOut()。这取决于每次迭代执行的复杂程序。
还可以考虑使用JQuery/AJAX异步执行。
- 创建一个循环来简化HTML和CSS代码
- 如何使用“;getElementById”;在一个循环中
- jQuery触发器事件在一个循环中多次出现
- js:如何制作一个循环,将20个不同的东西添加到一个对象中
- 每第n个图像具有下一个循环的余数偏移
- CasperJS-如何治疗'单击'在一个循环中
- 我怎样才能把它变成一个循环,而不是一个递归函数
- 在一个循环中读取xml文件的所有节点
- ajax 返回带有循环的图像长度,检查最后一个循环
- JQuery Slider动画只发生一个循环
- JQuery 滑动一个表格形成,另一个循环滑动
- 如何跟踪有序列表中的顺序,该列表具有一个循环列表项,提供不同的参数
- Node.js:如何在一个循环中使用回调调用方法
- 我将如何批量添加大量 jquery 脚本,在一个循环中只有一个细微的差异
- 循环遍历 Angular HTTP 请求中的对象,等到响应后再运行下一个循环
- 从嵌套循环 Javascript 中的一个循环访问参数
- jQuery点击函数在for循环中只在最后一个循环中工作
- 我可以为 4 个不同的下拉列表创建一个循环吗?
- 翡翠模板在一个循环中有多个数组
- 仅使用一个循环生成嵌套数组