Я работаю над инструментом, который позволяет пользователям вводить регулярное выражение для поиска и замены, а затем мой инструмент выполнит этот поиск и замену и вернет измененный текст. Однако недавно я столкнулся с ситуацией, когда поиск и замена просто зависли, поэтому я решил, что, вероятно, будет лучше каким-то образом обнаружить проблемы с сопоставлением регулярных выражений и прервать выполнение по прошествии определенного времени.

Я проверил, и то, что мне удалось найти с помощью этого ответа, заключалось в том, что проблема, с которой я столкнулся, была вызвана «катастрофический возврат». Это идеально знать, потому что таким образом я могу сделать минимальный рабочий пример того, где что-то идет не так, но не идеально, если решение для изменить регулярное выражение невозможно, потому что у меня нет контроля над вводом регулярного выражения пользователя (и я не могу написать достаточно продвинутый парсер регулярных выражений, чтобы ограничить использование регулярного выражения пользователем, чтобы исключить такие ситуации).

Поэтому, пытаясь решить эту проблему, я попытался использовать обещания, как это предлагается в этом ответе. В этом примере я сделал «катастрофическую» строку соответствия просто достаточно длинной, чтобы эффект зависал на моей вкладке на несколько секунд без полного сбоя вкладки. Хотя разные компьютерные спецификации могут давать разные результаты, я не уверен.

Только одно предупреждение: Выполнение этого кода может привести к зависанию вашей текущей вкладки. ПОЖАЛУЙСТА, убедитесь, что вы не написали частичный ответ при выполнении этого кода, так как это может привести к потере работы.

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 секунды)? Я лучше убью поиск и замену регулярных выражений, чем полностью сломаю инструмент, что приведет к потере работы для пользователя.

2
Joeytje50 11 Фев 2021 в 14:04

1 ответ

Лучший ответ

Я рекомендую использовать 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 не блокируется, пока рабочий работает.

В случае длительного рабочего процесса вы можете добавить код:

2
Peter Thoeny 12 Фев 2021 в 18:35