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

Can I detect a hanging JS function and abort?

我正在开发一个允许用户输入正则表达式进行查找和替换的工具,然后我的工具将执行该查找和替换以及 return 更改后的文本。然而,我最近 运行 遇到了查找和替换只是冻结的情况,所以我决定最好以某种方式检测正则表达式匹配的问题,并在经过一定时间后中止。

我已经四处查看,我无法使用 this answer was that the problem I'm experiencing was called 'catastrophic backtracking'. That's ideal to know, because that way I can make a minimal working example of where it goes wrong, however not ideal if the solution to change the regular expression 找到的内容,因为我无法控制用户的正则表达式输入(而且我无法编写足够高级的正则表达式解析器限制用户的正则表达式使用以排除这种情况。

因此,为了解决这个问题,我尝试使用 promises,如 中所建议的那样。在本示例中,我已将 'catastrophic' 匹配字符串 设置为 足够长的时间,足以让效果挂起我的选项卡几秒钟,而不会完全破坏选项卡。不过,不同的计算机规格可能会看到不同的结果,我不确定。

请注意:执行此代码可能会冻结您当前的选项卡。请确保您在执行此代码时没有编写部分答案,因为它可能会导致工作丢失。

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 秒,然后在控制台中显示“Promise Resolved”。

我现在的问题是:如果执行时间太长(在示例中:超过 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 执行(可能很长 运行ning)正则表达式匹配:

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 线程在 worker 工作时是非阻塞的。

如果是长 运行ning worker,您可以将代码添加到:

  • 使用 myWorker.terminate() 粗暴地终止网络工作者,然后重新启动它——参见 Is it possible to terminate a running web worker?
  • 或者,尝试从 web worker 范围内close()——见