Jsdom как «опросить наличие определенного элемента».?

#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:

Здесь пара недоразумений:

  1. Вызов elementOnPage(querySelector(...)) выполняется один раз (если только сам этот код не находится в каком-либо цикле или обработчике событий). Если элемент DOM существует во время выполнения, он немедленно возвращается, в противном случае он никогда больше не выполнит селектор, поэтому ваша функция будет выполняться бесконечно с null в качестве аргумента, который он был предоставлен.

  2. В 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 мс (хотя фактическое выполнение не будет таким точным). Пока вы делаете это с интервалом.