我有一个需要在客户端运行的CPU密集型任务。理想情况下,我希望能够使用jQuery调用该函数并触发进度事件,以便更新UI。
我知道JavaScript不支持线程,但我看过一些有前途的文章尝试使用setTimeout模拟线程。
对于这种情况,最好的方法是什么?谢谢。
我有一个需要在客户端运行的CPU密集型任务。理想情况下,我希望能够使用jQuery调用该函数并触发进度事件,以便更新UI。
我知道JavaScript不支持线程,但我看过一些有前途的文章尝试使用setTimeout模拟线程。
对于这种情况,最好的方法是什么?谢谢。
基本上,你想要做的就是将操作分成几个部分。假设你有10,000个要处理的项目,将它们存储在列表中,然后使用小延迟在每个调用之间处理一小部分。这里是一个简单的结构可以使用:
function performTask(items, numToProcess, processItem) {
var pos = 0;
// This is run once for every numToProcess items.
function iteration() {
// Calculate last position.
var j = Math.min(pos + numToProcess, items.length);
// Start at current position and loop to last position.
for (var i = pos; i < j; i++) {
processItem(items, i);
}
// Increment current position.
pos += numToProcess;
// Only continue if there are more items to process.
if (pos < items.length)
setTimeout(iteration, 10); // Wait 10 ms to let the UI update.
}
iteration();
}
performTask(
// A set of items.
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'],
// Process two items every iteration.
2,
// Function that will do stuff to the items. Called once for every item. Gets
// the array with items and the index of the current item (to prevent copying
// values around which is unnecessary.)
function (items, index) {
// Do stuff with items[index]
// This could also be inline in iteration for better performance.
});
还要注意的是,Google Gears支持在单独的线程上进行工作。Firefox 3.5也引入了自己的worker来完成同样的工作(虽然它们遵循W3标准,而Google Gears使用自己的方法)。
最近我遇到了一个类似的问题,需要在计算一些数据以展示时保持UI线程的空闲状态。
我编写了一个名为Background.js的库来处理几个场景: 一个基于WorkerQueue库的顺序后台队列,一个调用每个定时器的作业列表,以及一个数组迭代器来帮助将工作分解成更小的块。这里提供了一些示例和代码: https://github.com/kmalakoff/background
祝你使用愉快!
let iterate = function (from, to, action, complete) {
let i = from;
let impl = function () {
action(i);
i++;
if (i < to) setTimeout(impl, 1);
else complete();
};
impl();
};
根据您的要求,您可以通过使用Gears轻松完成任务。Gears支持线程,可以实现您想要的功能。
如您所述,setTimeout是另一种选择。根据任务的类型,您可以将循环的每个迭代交给一个带有间隔的单独setTimeout调用,或者您可能需要将主算法的各个部分分开成单独的函数,以与每个迭代相同的方式调用每个函数。
run()
方法的内容都可以排队作为任务。任务可以重新排队以分批执行工作。您可以设置任务优先级,随时添加/删除它们,暂停/恢复整个队列等。与异步操作配合使用效果很好-我最初使用它是为了管理几个并发的XMLHttpRequests。基本用法非常简单:var taskQueue = new TaskQueue();
taskQueue.schedule("alert('hello there')");
.js文件中的头部注释提供了更高级的示例。
//// thread definition
function Thread( name ) {
for ( var i = 0; i < 5; i++ ) {
Print(i+' ('+name+')');
yield;
}
}
//// thread management
var threads = [];
// thread creation
threads.push( new Thread('foo') );
threads.push( new Thread('bar') );
// scheduler
while (threads.length) {
var thread = threads.shift();
try {
thread.next();
threads.push(thread);
} catch(ex if ex instanceof StopIteration) { }
}
输出结果为:
0 (foo) 0 (bar) 1 (foo) 1 (bar) 2 (foo) 2 (bar) 3 (foo) 3 (bar) 4 (foo) 4 (bar)
=)
不过我真的很喜欢你的JavaScript评估器,我会收藏它的! =)
- Blixt有一个功能(在我写作时是实验性的):
https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
看起来不是真正的多线程,而是以低于其他(UI)任务的优先级执行任务
因此解决方案可以是:
在这些超时中,UI可以频繁更新
我的最强建议是在操作期间显示一个简单的loading.gif动画。如果用户被“告知”需要一些时间,他们通常会接受一定的持续时间。
使用requestIdleCallback()
。
然后,您将利用浏览器的空闲时间来处理您的任务。
示例见: https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API