如何在没有浏览器冻结的情况下执行繁重的javascript代码?

时间:2012-07-25 18:50:18

标签: javascript html scheduling freeze

我有一个页面,在加载后执行繁重的javascript代码。为了防止页面在加载时冻结,我将执行间隔为两个“无执行”时间(使用超时),并且运行良好。

最近,我不得不添加额外的重量级javascript代码,这些代码可以在客户端操作时执行,但这些操作甚至可以在原始繁重的脚本执行完毕之前发生。这一次,间隔操作将无济于事,因为在一个脚本的“停机时间”,另一个脚本可以运行,反之亦然,这将导致浏览器冻结。

问题实际上更复杂,因为有多个这样的动作,每个动作执行不同的重脚本,并且每个脚本对于我希望它完成的速度与其他脚本相比具有不同的“优先级”的。

我的问题是,这种情况下的常见做法是什么?我试着想办法解决它,但我能想到的只是一个非常复杂的解决方案,就像在javascript中编写一个操作系统一样 - 即编写一个每X次执行一次的“管理器”代码(使用一个“中断”),并选择“切换到的上下文”(=现在应该运行哪个作业),等等......

然而,这对我来说听起来很复杂,我希望可能还有其他解决方案。我的问题听起来像是我以前认为很多人偶然发现的问题,所以即使唯一的解决方案是我建议的,我也会假设某人已经写过它,或者有一些图书馆支持这个。

非常感谢任何帮助。谢谢。


==编辑==
通过“重代码”,我的意思是例如对大量元素的DOM操作。

2 个答案:

答案 0 :(得分:0)

您需要考虑将UI /问题域定义为一组异步任务。在我为您制定更好的答案之前,我需要进一步了解http://alexmaccaw.com/posts/async_ui

答案 1 :(得分:0)

如果您不想阻止脚本,可以使用web workers。有关详细介绍,请参阅MDN: Using web workers。请注意,Web工作者仍然是相对新的,大多数浏览器都不支持。

但是,如果您想支持所有浏览器为您的“重脚本”添加某种优先级,您应该自己定义一些内容,例如:

function WorkerQueue(this_argument){
    this.queue = [];
    this.this_argument = this_argument;
    this.priority = 1;
}

WorkerQueue.prototype.enqueue = function(callback){
    this.queue.push(callback);
}

WorkerQueue.prototype.dequeue = function(){
    return this.queue.splice(0,1)[0];   
}

function WorkerPool(){
    this.pool = [];
    this.status = "running";
    this.timeout = null;
}

WorkerPool.prototype.addWorker = function(this_argument){
    this.pool.push(new WorkerQueue(this_argument));
    return this.pool[this.pool.length - 1];
}

WorkerPool.prototype.nextTask = function(){
    var max_priority = 0;
    var max_priority_task = this.pool.length;
    for(var i = 0; i < this.pool.length; ++i){
        if(this.pool[i].priority > max_priority && this.pool[i].queue.length !== 0){
            max_priority = this.pool[i].priority;
            max_priority_task = i;
        }
    }
     // pool is empty or all tasks have an invalid priority  
    if(max_priority_task === this.pool.length)
        return;

    if(this.pool[max_priority_task].this_argument)
        this.pool[max_priority_task].dequeue().apply(this.pool[max_priority_task].this_argument);
    else
        this.pool[max_priority_task].dequeue().apply();

    if(this.status !== "running")
        return;
    this.timeout = setTimeout(function(t){return function(){t.nextTask();};}(this),1000);
}

var Workers = new WorkerPool();
var worker1 = Workers.addWorker();
worker1.enqueue(function(){
    console.log("Hello");
});
worker1.enqueue(function(){
    console.log("World");
});

var worker2 = Workers.addWorker();
worker2.priority = 2;
worker2.this_argument = worker2;
worker2.enqueue(function(){
    console.log("Worker 2 - changing priority");
    this.priority = .2;
});
worker2.enqueue(function(){
    console.log("Worker 2 - after change"); 
});

Workers.nextTask();

Demo

在这种情况下,每个“重脚本”都是一个工人,它基本上是一个任务队列。您可以使用addWorker在池中创建新工作线程,并使用worker.enqueue(callback)将任务添加到特定工作线队列。