слишком много рекурсии при переносе setTimeout () в веб-работнике - PullRequest
0 голосов
/ 01 апреля 2020

Я пытаюсь обернуть функцию self.setTimeout () внутри веб-работника (поэтому нет собственного оконного объекта). Я подумал, что было бы довольно просто использовать синтаксис метода apply:

window.setTimeout = function ( /**/) {
    return setTimeout.apply(self,arguments); 
};

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

Однако средняя строка кода в конечном итоге выдает ошибку "слишком много рекурсии". Похоже, что вызов этого кода каким-то образом нарушает описанный здесь механизм c: Будет ли рекурсивный вызов функции 'setTimeout' в конечном итоге уничтожить JS Engine? , который разрушает предыдущий контекст функции и делает его фактически рекурсивным. На всякий случай, это спецификация браузера c: протестировано в Firefox 74.0 (64-бит).

Некоторый фон

На всякий случай, если кому-то интересно почему я пытаюсь это сделать:

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

К сожалению, код опирается на некоторую библиотеку, которая в Поворот полагается на наличие окна и документа, иначе он не будет инициализирован.

В качестве обходного пути я использую Mock Dom внутри рабочего. Поскольку я на самом деле не хочу делать манипуляции с DOM, я просто добавляю любые функции, отсутствующие в mock dom, самым простым способом. Но по какой-то причине библиотека в какой-то момент явно вызывает window.setTimeout (), а не просто setTimeout () - поэтому мне нужно добавить эту функцию в объект фиктивного окна, и она должна работать правильно.

Обновление

Спасибо Александру Сенгесу за указание на ошибку, которую эта функция на самом деле будет вызывать сама.

Решение внутри работающего веб-приложения:

window.setTimeout = function ( /**/) {
    return DedicatedWorkerGlobalScope.prototype.setTimeout.apply(self,arguments); 
};

Итак, мне нужно написать весь путь функции, чтобы не вызывать себя из window.setTimeout.

Обновление 2

На самом деле, как другой ответ Кайидо указал, что моя оригинальная идея должна была работать нормально сама по себе. Причина, по которой произошло слишком много рекурсии, заключалась в том, что я допустил ошибку в какой-то другой части кода, которая эффективно скопировала self.setTimeout = window.setTimeout - таким образом, вызывая setTimeout===self.setTimeout===window.setTimeout, поэтому функция window.setTimeout внезапно стала рекурсивной без намерения.

Ответы [ 2 ]

1 голос
/ 01 апреля 2020

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

Например:

const recursive = (x) => {
    console.log(x);
    recursive(x+1);
}

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

Теперь JS защищает нас от этого, ограничивая глубина стека вызовов.

В вашем примере проблема связана с тем, что вы случайно создали рекурсивную функцию. Когда вы вызовете свой новый window.setTimeout, он вызовет setTimeout.apply, который ссылается на саму новую функцию, поэтому он снова вызывает себя и т.д. c. Одним из исправлений будет извлечение предыдущего метода в новую переменную и его последующий вызов:

window.oldTimeout = window.setTimeout;
window.setTimeout = function ( /**/) {
    return oldTimeout.apply(self,arguments); 
};

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

0 голосов
/ 01 апреля 2020

Ваш код должен работать просто отлично:

const worker_script = `
  const window = {};
  window.setTimeout = function ( /**/) {
    return setTimeout.apply(self,arguments); 
  };
  window.setTimeout( (val)=>postMessage('done ' + val), 200, 'ok');
`;
const blob = new Blob( [ worker_script ] );
const url = URL.createObjectURL( blob );
const worker = new Worker( url );
worker.onmessage = e => console.log( e.data );
worker.onerror = console.error;

Чтобы получить эту ошибку, вы, вероятно, установили window в self:

const worker_script = `
  const window = self;
  window.setTimeout = function ( /**/) {
    return setTimeout.apply(self,arguments); 
  };
  window.setTimeout( (val)=>postMessage('done ' + val), 200, 'ok');
`;
const blob = new Blob( [ worker_script ] );
const url = URL.createObjectURL( blob );
const worker = new Worker( url );
worker.onmessage = e => console.log( e.data );
worker.onerror = console.error;

Если это так, вам даже не нужно ничего переписывать:

const worker_script = `
  const window = self;
  window.setTimeout( (val)=>postMessage('done ' + val), 200, 'ok');
`;
const blob = new Blob( [ worker_script ] );
const url = URL.createObjectURL( blob );
const worker = new Worker( url );
worker.onmessage = e => console.log( e.data );
worker.onerror = console.error;
...