在html/javascript中长时间运行的代码

dev*_*per 2 javascript web

我需要在按钮单击时在浏览器中运行算法.在javascript中对它进行编码非常复杂,而且速度非常慢.有没有推荐的架构呢?理想情况下,我想用C++或Python编写代码,但我想在按钮点击时无法在浏览器中运行它.那么,我的下一个最佳选择是什么?

我不能让它在服务器端运行,因为页面上会有1000次点击,这将导致来回过多的通信.

T.J*_*der 5

那么,我的下一个最佳选择是什么?

使用Web worker(规范,MDN),以便计算不在主UI线程上运行.工作人员甚至可以将更新发布到主线程以显示进度.


从您对该问题的评论:

它需要是同步操作,即点击...

这并不遵循,如果它正在进行繁重的工作,你真的希望它是同步的,你将锁定浏览器的UI.

如果您在处理过程中需要阻止进一步的点击,只需在按钮运行时禁用它.

这里有一个例子,它可以计算出10亿个因子(即使在现代浏览器上也需要一些时间),每百万个工人的更新:

HTML:

<input type="button" class="the-btn" value="Click To Start">
<div>
    <div class="progress-wrapper">
        <div class="progress-bar"></div>
    </div>
    <div class="progress-counter">-</div>
</div>
<div class="result"></div>
Run Code Online (Sandbox Code Playgroud)

CSS:

.progress-wrapper {
    border: 1px solid black;
    display: inline-block;
    width: 70%;
    height: 1em;
}
.progress-bar {
    display: inline-block;
    width: 0;
    background-color: blue;
    height: 1em;
}
.progress-counter {
    display: inline-block;
}
Run Code Online (Sandbox Code Playgroud)

factorial_worker.js:

self.onmessage = function(e) {
    if (e.data && e.data.type === "start") {
        var n = 0, max = 1000000000, result = 0;
        while (n < max) {
            if (n % 1000000 === 0) {
                self.postMessage({type: "progress", progress: (n / max) * 100});
            }
            result += n;
            ++n;
        }
        self.postMessage({type: "done", result: result});
    }
};
Run Code Online (Sandbox Code Playgroud)

您在页面中的主要脚本:

// Get the worker
var worker = new Worker("factorial_worker.js");

// Get our various elements
var btn = document.querySelector(".the-btn");
var progressBar = document.querySelector(".progress-bar");
var progressCounter = document.querySelector(".progress-counter");
var result = document.querySelector(".result");

function setProgress(progress) {
    var percent = progress.toFixed(2) + "%";
    console.log("Progress: " + percent);
    progressCounter.innerHTML = percent;
    progressBar.style.width = percent;
}

// Handle clicks
btn.addEventListener("click", function() {
    // Disable the button and tell the worker to get started
    worker.postMessage({type: "start"});
    result.innerHTML = "Working...";
    btn.disabled = true;
});

// Handle a message from the worker
worker.onmessage = function(e) {
    switch (e.data.type) {
        case "progress":
            setProgress(e.data.progress);
            break;
        case "done":
            // Re-enable the button
            btn.disabled = false;
            setProgress(100);
            result.innerHTML = "Result: " + e.data.result;
            break;
    }
};
Run Code Online (Sandbox Code Playgroud)

实例(有一个技巧将工作者嵌入页面,因为我们无法在Stack Snippets上执行外部文件):

// <ignore> Ignore this bit, it's just because we can't have a separate file in Stack Snippets
var blob = new Blob([
    document.querySelector(".the-worker").textContent
], { type: "text/javascript" });
// </ignore>

// Get the worker
// In your own code, you'd refer to a JavaScript file here:
// var worker = new Worker("my_worker_script.js");
var worker = new Worker(window.URL.createObjectURL(blob));

// Get our various elements
var btn = document.querySelector(".the-btn");
var progressBar = document.querySelector(".progress-bar");
var progressCounter = document.querySelector(".progress-counter");
var result = document.querySelector(".result");

function setProgress(progress) {
    var percent = progress.toFixed(2) + "%";
    progressCounter.innerHTML = percent;
    progressBar.style.width = percent;
}

// Handle clicks
btn.addEventListener("click", function() {
    // Disable the button and tell the worker to get started
    worker.postMessage({type: "start"});
    result.innerHTML = "Working...";
    btn.disabled = true;
});

// Handle a message from the worker
worker.onmessage = function(e) {
    switch (e.data.type) {
        case "progress":
            setProgress(e.data.progress);
            break;
        case "done":
            // Re-enable the button
            btn.disabled = false;
            setProgress(100);
            result.innerHTML = "Result: " + e.data.result;
            break;
    }
};
Run Code Online (Sandbox Code Playgroud)
.progress-wrapper {
    border: 1px solid black;
    display: inline-block;
    width: 70%;
    height: 1em;
}
.progress-bar {
    display: inline-block;
    width: 0;
    background-color: blue;
    height: 1em;
}
.progress-counter {
    display: inline-block;
}
Run Code Online (Sandbox Code Playgroud)
<input type="button" class="the-btn" value="Click To Start">
<div>
    <div class="progress-wrapper">
        <div class="progress-bar"></div>
    </div>
    <div class="progress-counter"></div>
</div>
<div class="result"></div>
<script class="the-worker" type="javascript/worker">
// This script won't be parsed by JS engines because its type is javascript/worker.
self.onmessage = function(e) {
    if (e.data && e.data.type === "start") {
        var n = 0, max = 1000000000, result = 0;
        while (n < max) {
            if (n % 1000000 === 0) {
                self.postMessage({type: "progress", progress: (n / max) * 100});
            }
            result += n;
            ++n;
        }
        self.postMessage({type: "done", result: result});
    }
};
</script>
Run Code Online (Sandbox Code Playgroud)