我可以检测到一个挂起的JS函数和中止吗?



我正在开发一个工具,该工具允许用户为find&replace输入正则表达式,然后我的工具将执行该find&replace并返回更改后的文本。但是,我最近遇到了find&replace冻结的情况,因此我认为最好以某种方式检测正则表达式匹配的问题,并在经过一定时间后中止。

我已经检查过了,我能够使用这个答案找到的是我正在经历的问题被称为"灾难性回溯"。知道这一点是很理想的,因为这样我就可以做一个最小的工作示例来说明哪里出错了,但是如果改变正则表达式的解决方案是不可能的,那就不理想了,因为我无法控制用户的正则表达式输入(而且我无法编写一个足够高级的正则表达式解析器来限制用户的正则表达式使用,以排除这种情况)。

因此,为了解决这个问题,我尝试使用承诺,正如这个答案所建议的那样。在这个例子中,我将'灾难性'匹配字符串设置为足够长,以便在不完全崩溃的情况下将标签挂起几秒钟。不同的电脑规格可能会看到不同的结果,但我不确定。

只是一个提示:执行这段代码可能会冻结当前选项卡。请确保在执行此代码时没有编写部分答案,因为它可能会导致工作丢失。

var PTest = function () {
return new Promise(function (resolve, reject) {
setTimeout(function() {
reject();
}, 100)
"xxxxxxxxxxxxxxxxxxxxxxxxx".match(/(x+x+)+y/)
resolve();
});
}
var myfunc = PTest();
myfunc.then(function () {
console.log("Promise Resolved");
}).catch(function () {
console.log("Promise Rejected");
});

在我的电脑上,这会导致选项卡冻结大约4秒,然后显示"承诺已解决";

我现在的问题是:有可能"流产"吗?像这样的脚本的执行,如果执行时间太长(在本例中:超过0.2秒)?我宁愿终止正则表达式查找和替换,也不愿完全崩溃这个工具,因为这会导致用户的工作损失。

我推荐使用Web Worker,因为它会在自己的沙盒中运行:https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API

Web Worker是自己的脚本,你需要包含在你的JavaScript中,比如:

var worker = new Worker('/path/to/run-regex.js');

以下是未经测试的代码,但应该可以让您继续。

您的run-regex.js执行(可能长时间运行)正则表达式匹配:

function regexMatch(str, regexStr, callback) {
let regex = new RegExp(regexStr);
let result = str.match(regex);
callback(result, '');
}
onmessage = function(e) {
let data = e.data;
switch (data.cmd) {
case 'match':
regexMatch(data.str, data.regex, function(result, err) {
postMessage({ cmd: data.cmd, result: result, err: err });
});
break;
case 'replace':
//regexMatch(data.str, data.regex, data.replace, function(result, err) {
//  postMessage({ cmd: data.cmd, result: result, err: err });
//});
break;
default:
break;
postMessage({ err: 'Unknown command: ' + data.cmd });
}
}

在你自己的脚本中,加载Web Worker,并添加一个事件监听器:

if(window.Worker) {
const myWorker = new Worker('/path/to/run-regex.js');
myWorker.onmessage = function(e) {
let data = e.data;
if(data.err) {
// handle error
} else {
// handle match result using data.result;
}
}
function regexMatch(str, regex) {
let data = { cmd: 'match', str: str, regex: regex.toString() };
myWorker.postMessage(data);
}
regexMatch('xxxxxxxxxxxxxxxxxxxxxxxxx', /(x+x+)+y/);
} else {
console.log('Your browser does not support web workers.');
}

这样,当工作线程工作时,主JavaScript线程就不会阻塞了。

对于长时间运行的工作线程,您可以添加代码:

  • 使用myWorker.terminate()不优雅地终止web worker,然后重新启动它——参见是否有可能终止正在运行的web worker?
  • 或者,尝试从web worker范围内close()-参见JavaScript web worker - close() vs terminate()

最新更新