#typescript #jsdom
#typescript #jsdom
Вопрос:
В документах jsdom https://github.com/jsdom/jsdom при асинхронной загрузке скрипта опрос на наличие определенного элемента рекомендуется в качестве метода, позволяющего убедиться, что dom загружает то, что вам нужно.
Приведенный ниже код является моей интерпретацией этого:
const elementOnPage = (arg: HTMLElement | null): HTMLElement => {
let tried = arg;
while (tried === null) {
// debugger;
console.log("nothing yet");
tried = arg;
}
return tried;
};
затем вызовите функцию следующим образом:
...
await elementOnPage(
dom.window.document.querySelector("#some element that I know will eventually be loaded onto the page")
);
...
Несмотря на то, что я знаю, что этот элемент будет загружен в конечном итоге, это вызывает бесконечный цикл. Похоже, что аргумент может быть оценен как нулевой в первый раз, а затем установлен в значение null каждый последующий раз, но я не уверен. В любом случае, я также пытался использовать
dom.window.document.getElementsByClassName("some class that will eventually have member elements")
и проверка того, что результирующая длина коллекции не равна 0, но это возвращает тот же бесконечный цикл. Я не думаю, что проблема заключается в методе выбора dom. Итак, что вызывает этот бесконечный цикл и / или есть ли лучший способ опроса элемента?
Ответ №1:
Здесь пара недоразумений:
-
Вызов
elementOnPage(querySelector(...))
выполняется один раз (если только сам этот код не находится в каком-либо цикле или обработчике событий). Если элемент DOM существует во время выполнения, он немедленно возвращается, в противном случае он никогда больше не выполнит селектор, поэтому ваша функция будет выполняться бесконечно сnull
в качестве аргумента, который он был предоставлен. -
В JavaScript такие циклы, как
while
, блокируют (не включая рабочие, которые здесь не связаны), поэтому вы в основном блокируете поток. Даже если бы вы повторно запускалиquerySelector
изнутри цикла, это привело бы к бесконечному циклу.
Для выполнения опроса без блокировки выполнения JS вы хотите использовать setInterval
или setTimeout
.
Кроме того, ожидаемая функция должна возвращать Promise
(технически вы можете await
что угодно, но обещание — это ваш способ сделать его полезным).
Вот непроверенный пример:
const elementOnPage = (query: string, timeout: number = 10000): Promise<HTMLElement | null> => {
return new Promise((resolve, reject) => {
const startTime = Date.now();
const tryQuery = () => {
const elem = dom.window.document.querySelector(query);
if (elem) resolve(elem); // Found the element
else if (Date.now() - startTime > timeout) resolve(null); // Give up eventually
else setTimeout(tryQuery, 10); // check again every 10ms
}
tryQuery(); // Initial check
});
};
// Elsewhere:
const elem = await elementOnPage("#some_element");
Комментарии:
1. Я вижу. Имеет смысл, потому что я пытался передать обещание, которое возвращает queryselector вместо самого селектора, но, похоже, оно все еще возвращало бесконечный цикл. Итак, как бы мне определить используемый интервал? Просто произвольно? Предположим, я использовал интервал в 2 секунды. Этого должно быть достаточно для загрузки скриптов на странице, но предположим, что при очень медленном соединении это не так?
2. Я добавил пример. Вы могли бы сделать интервал для проверки очень маленьким, например, каждые 10 мс или даже 1 мс (хотя фактическое выполнение не будет таким точным). Пока вы делаете это с интервалом.