假设我有一个长期运行的JavaScript进程,可以通过单击a来启动 按钮。一旦运行,它就会冻结浏览器。该过程包括一个长循环 重复一些迭代占用相对较少时间的工作量。这是 这种类似循环的'的简化版本。过程:
<html>
<head>
<script>
var Process = function(start) {
this.start = start;
}
Process.prototype.run = function(stop) {
// Long-running loop
for (var i = this.start; i < stop; i++) {
// Inside the loop there is some workload which
// is the code that is to be debugged
console.log(i);
}
}
var p = new Process(100);
window.onload = function() {
document.getElementById("start").onclick = function() {
p.run(1000000000);
}
}
</script>
</head>
<body>
<input id="start" type="button" value="Start" />
</body>
</html>
由于浏览器冻结,调试类似循环的脚本并不容易。避免浏览器冻结的一种替代方法是使用Web工作者。这种方法的缺点是Web worker的可调试性差:不支持像Firebug这样的工具。
有没有办法在保持debuggabilty的同时避免浏览器冻结?如果是,则可以调试脚本,直到它稳定并委派给Web worker进行生产。
答案 0 :(得分:1)
事实证明,有一种方法可以实现这一目标。使用队列数据结构 (例如http://code.stephenmorley.org/javascript/queues/),间隔计时器和一些小的计时器 修改原始过程的控制流程可以构建一个不具备的GUI 冻结浏览器,使进程完全可调试,甚至允许其他功能 喜欢踩踏,停顿和停止。
这是怎么回事:
<html>
<head>
<script src="http://code.stephenmorley.org/javascript/queues/Queue.js"></script>
<script>
// The GUI controlling process execution
var Gui = function(start) {
this.timer = null; // timer to check for inputs and/or commands for the process
this.carryOn = false; // used to start/pause/stop process execution
this.cmdQueue = new Queue(); // data structure that holds the commands
this.p = null; // process instance
this.start = start;
this.i = start; // input to the modified process
}
Gui.prototype = {
/**
* Receives a command and initiates the corresponding action
*/
executeCmd: function(cmd) {
switch (cmd.action) {
case "initialize":
this.p = new Process(this);
break;
case "process":
this.p.run(cmd.i);
break;
}
},
/*
* Places next command into the command queue
*/
nextInput: function() {
this.cmdQueue.enqueue({
action: "process",
i: this.i++
});
}
}
// The modified loop-like process
var Process = function(gui) {
this.gui = gui;
}
Process.prototype.run = function(i) {
// The workload from the original process above
console.log(i);
// The loop itself is controlled by the GUI
if (this.gui.carryOn) {
this.gui.nextInput();
}
}
// Event handlers for GUI interaction
window.onload = function() {
var gui = new Gui(100);
document.getElementById("init").onclick = function() {
gui.cmdQueue.enqueue({ // first command will instantiate the process
action: "initialize"
});
// Periodically check the command queue for commands
gui.timer = setInterval(function() {
if (gui.cmdQueue.peek() !== undefined) {
gui.executeCmd(gui.cmdQueue.dequeue());
}
}, 4);
}
document.getElementById("step").onclick = function() {
gui.carryOn = false; // execute just one step
gui.nextInput();
}
document.getElementById("run").onclick = function() {
gui.carryOn = true; // (restart) and execute until further notice
gui.nextInput();
}
document.getElementById("pause").onclick = function() {
gui.carryOn = false; // pause execution
}
document.getElementById("stop").onclick = function() {
gui.carryOn = false; // stop execution and clean up
gui.i = gui.start;
clearInterval(gui.timer)
while (gui.cmdQueue.peek()) {
gui.cmdQueue.dequeue();
}
}
}
</script>
</head>
<body>
<input id="init" type="button" value="Init" />
<input id="step" type="button" value="Step" />
<input id="run" type="button" value="Run" />
<input id="pause" type="button" value="Pause" />
<input id="stop" type="button" value="Stop" />
</body>
</html>
虽然这种方法当然不适合所有可以想到的长期运行的脚本,但它确实如此 可以适应任何类似循环的场景。我用它来移植Numenta's HTM/CLA人为的 智能算法到浏览器。