在JavaScript中执行后台任务

54

我有一个需要在客户端运行的CPU密集型任务。理想情况下,我希望能够使用jQuery调用该函数并触发进度事件,以便更新UI。

我知道JavaScript不支持线程,但我看过一些有前途的文章尝试使用setTimeout模拟线程。

对于这种情况,最好的方法是什么?谢谢。

11个回答

40

基本上,你想要做的就是将操作分成几个部分。假设你有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使用自己的方法)。


1
谢谢。这对我非常有效,但是我正在处理一个数据数组,所以这可能不是每个人的最佳解决方案。 - Rob
无法用于现有控件,如不支持分解任务的树视图,其中包括对每个节点进行迭代更新。 - Triynko

20

最近我遇到了一个类似的问题,需要在计算一些数据以展示时保持UI线程的空闲状态。

我编写了一个名为Background.js的库来处理几个场景: 一个基于WorkerQueue库的顺序后台队列,一个调用每个定时器的作业列表,以及一个数组迭代器来帮助将工作分解成更小的块。这里提供了一些示例和代码: https://github.com/kmalakoff/background

祝你使用愉快!


10

如果您可以强制使用浏览器,或者您已经知道它是Firefox的新版本,那么您可以使用Mozilla的新WebWorkers。它允许您生成新线程。


4
以下是我对这个问题的解决方案,以防有人需要一个简单可复制粘贴的代码片段:
    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();
    };

1
那救了命!! - AnkitRox

4

根据您的要求,您可以通过使用Gears轻松完成任务。Gears支持线程,可以实现您想要的功能。

如您所述,setTimeout是另一种选择。根据任务的类型,您可以将循环的每个迭代交给一个带有间隔的单独setTimeout调用,或者您可能需要将主算法的各个部分分开成单独的函数,以与每个迭代相同的方式调用每个函数。


+1,但是“workers”不完全等同于其他环境中所称的“线程”。 - Javier
Firefox 3.5同样具有W3标准化的workers。请查看我的回答获取链接。 - Blixt

3
伟大的答案,Kevin!我几年前写了类似的东西,尽管不太复杂。如果有人需要,源代码在此处:http://www.leapbeyond.com/ric/jsUtils/TaskQueue.js 任何带有run()方法的内容都可以排队作为任务。任务可以重新排队以分批执行工作。您可以设置任务优先级,随时添加/删除它们,暂停/恢复整个队列等。与异步操作配合使用效果很好-我最初使用它是为了管理几个并发的XMLHttpRequests。基本用法非常简单:
var taskQueue = new TaskQueue();
taskQueue.schedule("alert('hello there')");

.js文件中的头部注释提供了更高级的示例。


1
这是一个非常基本的JavaScript线程创建示例。 请注意,中断线程函数(yield指令)由您自己控制。如果您愿意,可以使用setTimeout代替我的while循环来定期运行调度程序。 还要注意,此示例仅适用于JavaScript版本1.7+(firefox 3+) 您可以在此处尝试:http://jslibs.googlecode.com/svn/trunk/jseval.html
//// 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) 

这真的很棒,尽管除非用户可以被强制使用Firefox 3+,否则不可行。 =) 不过我真的很喜欢你的JavaScript评估器,我会收藏它的! =) - Blixt
嗨,谢谢!也许你可以看一下jslibs项目:http://code.google.com/p/jslibs - Franck Freiburger

0

有一个功能(在我写作时是实验性的):

https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback

看起来不是真正的多线程,而是以低于其他(UI)任务的优先级执行任务

因此解决方案可以是:

  1. 使您的任务异步
  2. 使用requestIdleCallback启动您的任务
  3. 在您的任务中使用setTimeout()和微时间循环,例如在某些主循环和其内部逻辑之间

在这些超时中,UI可以频繁更新


0

我的最强建议是在操作期间显示一个简单的loading.gif动画。如果用户被“告知”需要一些时间,他们通常会接受一定的持续时间。

Ajaxload - Ajax loading gif generator


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接