如何在不冻结浏览器的情况下执行大量javascript代码

How to execute heavy javascript code without the browser freezing?

本文关键字:执行 javascript 代码 情况下 冻结 浏览器      更新时间:2024-03-16

我有一个页面,它在加载后执行大量javascript代码。为了防止页面在加载时冻结,我将执行间隔成批,中间有一些"无执行"时间(使用超时),一切都很顺利。

最近,我不得不添加额外的重型javascript代码,这些代码可以在客户端操作时执行,但这些操作甚至可以在原始重型脚本执行之前发生。这一次,间隔操作没有帮助,因为在一个脚本的"停机时间",另一个脚本可以运行,反之亦然,这将导致浏览器冻结。

事实上,问题更为复杂,因为有多个这样的操作,每个操作都执行不同的重脚本,而且与其他脚本相比,每个脚本都有不同的"优先级"来决定我希望它以多快的速度完成。

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

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

如有任何帮助,我们将不胜感激。非常感谢。


==编辑==
所谓"重代码",我指的是例如对大量元素的DOM操作。

您需要考虑将UI/Problem域定义为一组异步任务。以下是更多见解http://alexmaccaw.com/posts/async_ui直到我为你想出一个更好的答案。

如果你不想阻止你的脚本,你可以使用web工作者。请参阅MDN:使用网络工作者以获得一个很好的介绍。请注意,网络工作者仍然相对较新,大多数浏览器都不支持。

然而,如果你想支持所有浏览器为你的"重脚本"添加某种优先级,你应该自己定义一些东西,例如:

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();

演示

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