在Javascript中执行后台任务

时间:2009-07-21 15:57:23

标签: javascript multithreading

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

我知道javascript不支持线程,但我看过一些有希望的文章尝试使用setTimeout来模仿线程。

最好的方法是什么?感谢。

9 个答案:

答案 0 :(得分:38)

基本上,您要做的是将操作分成几部分。因此,假设您要处理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 has support to do work on a separate thread。 Firefox 3.5还引入了its own workers that do the same thing(尽管它们遵循the W3 standard,而Google Gears则使用自己的方法。)

答案 1 :(得分:20)

我最近有一个类似的问题要解决,我需要保持我的UI线程免费,同时处理一些数据显示。

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

享受!

答案 2 :(得分:8)

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

答案 3 :(得分:4)

根据您的要求,您可以使用Gears轻松下车。 Gears支持线程,可以做你想要的。

正如您所提到的,setTimeout是另一种选择。根据任务的类型,您可以将循环的每次迭代移交给单独的setTimeout调用,其间有一些间距,或者您可能需要将主算法的各个部分分成单独的函数,这些函数可以逐个调用与您调用每次迭代的方式相同。

答案 4 :(得分:2)

很棒的回答凯文!几年前我写了类似的东西,虽然不太复杂。如果有人想要它,源代码就在这里:

http://www.leapbeyond.com/ric/jsUtils/TaskQueue.js

使用run()方法的任何内容都可以排队等任务。任务可以重新排队以执行块中的工作。您可以优先处理任务,随意添加/删除它们,暂停/恢复整个队列等。适用于异步操作 - 我最初的用途是管理多个并发的XMLHttpRequests。

基本用法非常简单:

var taskQueue = new TaskQueue();
taskQueue.schedule("alert('hello there')");

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

答案 5 :(得分:2)

以下是我对问题的解决方案,以防有人想要简单的可复制粘贴代码:

    var iterate = function (from, to, action, complete) {
        var i = from;
        var impl = function () {
            action(i);
            i++;
            if (i < to) setTimeout(impl, 1);
            else complete();
        };
        impl();
    };

答案 6 :(得分:1)

我最强烈的建议是在操作过程中显示一个简单的loading.gif。如果用户被“告知”可能需要一些时间,用户通常会接受一段时间。

Ajaxload - Ajax loading gif generator

答案 7 :(得分:1)

这是如何在JavaScript中创建线程的一个非常基本的示例。 请注意,由您决定是否中断线程函数(yeld指令)。如果需要,可以使用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) 

答案 8 :(得分:-1)

似乎这个问题已经在节点本身解决了。
需要包含在nodejs 0.10.4

中的child_process