Прокрутка Javascript через большой словарь дает невосприимчивый скрипт

54
5

Я делаю Firefox-аддон, который добавляет слова с другими словами в html-тексте. Этот код работает, но при повторении через огромный словарь я получаю ошибку, не отвечающую на скрипт.

Каков наилучший способ улучшить скорость этого цикла?

Разделение словаря на более мелкие объекты? Или установить функцию тайм-аута?

var brands = {"manykeys" : "manyvalues"};

function replaceWord(){
for (var key in brands){
htmlreplace(key, key + " (" + brands[key] + ")");
}
}
function htmlreplace(a, b, element) {
if (!element) element = document.body;
var nodes = element.childNodes;
for (var n=0; n<nodes.length; n++) {
if (nodes[n].nodeType == Node.TEXT_NODE) {
var r = new RegExp(a, 'g');
nodes[n].textContent = nodes[n].textContent.replace(r, b);
} else {
htmlreplace(a, b, nodes[n]);
}
}
}

replaceWord();

спросил(а) 2020-04-04T00:35:00+03:00 3 месяца назад
1
Решение
86

Есть некоторые соображения. Это зависит от того, что вы можете изменить или нет. Одним из больших улучшений, которые вы можете сделать, является использование массива вместо объекта key/value.

var brands = [
['manykeys0000','manyvalues0000'],
['manykeys0001','manyvalues0001'],
['manykeys0002','manyvalues0002'],
['manykeys0003','manyvalues0003'],
['manykeys0004', ...
];

function replaceWord(){
var i, n = brands.length;
for (i = 0; i < n; ++i) {
htmlreplace(brands[i][0], brands[i][0] + " (" + brands[i][1] + ")");
}
}

Пара других изменений также должна дать небольшое улучшение:

1.) Переместите nodes.length вне цикла.
2.) Если целесообразно передать document.body из replaceWord()

var body = document.body;

...
htmlreplace(brands[i][0], brands[i][0] + " (" + brands[i][2] + ")", body);

function htmlreplace(a, b, element) {
var nodes = element.childNodes, len = nodes.length;
for (var n=0; n < len; ++n) {

Комбинированный быстрый тест на Chrome и Firefox дал 30-40% увеличение скорости.

Другие изменения для тестирования:

Переместить var r = new RegExp(a, 'g'); для replaceWord() и передать его в качестве первого аргумента в htmlreplace() вместо a.

function replaceWord(){
var i, n = brands.length;
for (i = 0; i < n; ++i) {
var r = new RegExp(brands[i][0], 'g');
htmlreplace(r, brands[i].join(' (') + ')', elem);
}
}

Если вы играете с таймаутом, эта статья может представлять интерес. Оно использует

window.postMessage();

для реализации пользовательского setZeroTimeout() но не уверен, как это повлияет на ваш случай.

Помимо JSPerf и т.д., Используйте инструменты профилирования в браузере, например, в Chrome, что может быть лучше подходит для того, что вы делаете в своем коде.

ответил(а) 2020-04-04T00:54:36.004679+03:00 3 месяца назад
39

Узким местом в вашем коде не является размер словаря, если он действительно большой, но обход DOM.

Получайте текстовые узлы один раз, а затем работайте с ними.

var textnodes = $x("//text()", document.body)

function $x(p, context) {
if (!context) context = document;
var i, arr = [], xpr = document.evaluate(p, context, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (i = 0; item = xpr.snapshotItem(i); i++) arr.push(item);
return arr;
}

Вы должны увидеть значительное улучшение скорости.

ответил(а) 2020-04-04T00:35:00+03:00 3 месяца назад
39

Я не пробовал, но это может сработать:

function replaceWord(){
for (var key in brands){
(function(key) {
setTimeout(function() {
htmlreplace(key, key + " (" + brands[key] + ")");
}, 0);
})(key);
}
}

Идея заключается в том, что вы откладываете замену, когда у браузера есть время для этого, вместо того, чтобы делать это монолитно и заставляя браузер замораживаться, когда он думает.

ответил(а) 2020-04-04T00:35:00+03:00 3 месяца назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема