首页 > 解决方案 > 如何在javascript中等待具有超时的元素

问题描述

我正在寻找一种方法来等待元素在 DOM 中显示并在给定时间内未显示时抛出超时异常。我怎样才能做到这一点?我有这个想法,但肯定它不会起作用。

function waitForElement(element, timeout) {
    return new Promise((resolve, reject) => {
        const t = setTimeout(() => {
            let queryInterval = setInterval(() => {
                const isElementVisible = document.contains(element);
                if (!isElementVisible) {
                    clearInterval(queryInterval);
                    clearTimeout(t);
                    resolve(true);
                }
            }, 10);
            console.log("Timeout!");
            clearTimeout(t);
            reject("Time Out!");
        }, timeout);
    });
}

标签: javascriptjquery

解决方案


查看突变观察者 api

这是一个示例实现,它返回一个 Promise,如果该节点未在分配的时间内出现,则该 Promise 要么使用添加的节点进行解析,要么拒绝。

它通过设置一个 MutationObserver 来监视是否出现通过给定的testFn. 它设置了一个超时,断开观察者的连接并在给定的时间后拒绝。如果观察者在超时到期之前看到节点出现,它会清除超时并与节点一起解决。

// Waits for a node that passes testFn to appear and returns it.
// Gives up and rejects after 'wait' ms.
// Returns a Promise that resolves with the found node.
const waitForElement = (testFn, wait = 1000) => {

  return new Promise((resolve, reject) => {

    // create an observer using onDomChange (defined below)
    const observer = new MutationObserver(onDomChange);

    // watch the container for childList changes
    observer.observe(container, {
      childList: true
    });

    // set a countdown to give up and reject
    const timeout = setTimeout(
      () => {
        // when the timeout expires, stop watching…
        observer.disconnect();
        // and reject
        reject('Never showed up.')
      },
      wait // how long to wait before rejecting
    );

    // the callback for observed dom changes
    function onDomChange(mutations, observer) {
      // find an addedNode that passes the testFn in the mutations
      const node = mutations.map(m => [...m.addedNodes]).flat().find(testFn);

      // if we find a match…
      if (node) {
        // stop the rejection timeout
        clearTimeout(timeout);

        // stop observing
        observer.disconnect();

        // and resolve with the found node
        resolve(node);
      }
    }
  });
}

// convenience for creating a div with the given text
const d = (text) => {
  const el = document.createElement('div');
  el.className = 'bananas';
  el.innerText = text;
  return el;
}

// test the waitForElement with the given delay
async function go(delay) {
  const container = document.getElementById('container');

  // wait for the specified duration before adding a div with the delay as the text
  setTimeout(() => container.appendChild(d(delay)), delay);

  try {
    // wait for the element to appear
    const el = await waitForElement(n => n.innerText.endsWith(delay));
    // append some text to the content
    el.innerText += ' - found it.'
    // add a css class
    el.classList.add('found');
  } catch (e) {
    // log an error if it didn't appear in time
    console.error(`rejected for ${delay}`);
  }
}

// default wait time is 1000ms, so…
go(300); // under the 1000ms wait time. finds it.
go(5000); // takes too long. rejects
go(100); // finds it.
go(600); // finds it.
go(3000); // rejects
.bananas {
  margin-bottom: .5em;
  padding: 0.25em;
  background: #ffff99;
}

.found {
  background: tomato;
  color: bisque;
}
<div id="container"></div>


推荐阅读