【问题标题】:Is there a way to know if a link/script is still pending or has it failed有没有办法知道链接/脚本是否仍在等待或失败
【发布时间】:2017-02-11 00:05:36
【问题描述】:

我想从下面的 html 中知道,如果 link[rel=import]link[rel=stylesheet]imgscriptpending/loaded/failed/aborted 而无需添加事前和事件发生后的任何时候的监听器

<!DOCTYPE html>
<html>
<head>
    <title>App</title>
    <meta charset="utf-8">
    <link rel="import" href="template-bundle.html">
    <link rel="stylesheet" href="bundle.css">
</head>
<body>
    <header><img src="logo.png" alt="App logo"></header>
    <!-- Boilerplate... -->
    <script src="./app-bundle.js"></script>
</body>
</html>

换句话说:有没有提供类似于 Bluebird 的 isPending()isResolved()isRejected() 方法或常规 ES6 Promise 的接口?


额外问题:这是否可以使用 Service Worker 来实现?

由于 SW 可以拦截请求并知道它们的状态,我想知道我是否可以实现一个返回 Promise 的 API

  • 如果请求仍处于未决状态,则处于未决状态
  • 如果load 事件被触发则解决
  • 如果erroraborted 被解雇,
  • 将被拒绝。

感谢您的帮助


更新和解决方案:

感谢@pritishvaidya 和@guest271314 的回答,我能够使用MutationObserver 提出一个可行的解决方案,其中包括观察DOM 以添加资源节点(链接、img、脚本)并向它们添加承诺将如上所述解决

这很好用,唯一需要在任何其他资源之前将脚本标记内联到 &lt;head&gt; 中。 Here's an example

var resourceNodeSelector = 'link[href],script[src],img[src]';
function watchResource (n) {
    var url = n.href || n.src;

    if (!n.matches || !n.matches(resourceNodeSelector)) {
        return;
    }

    if (n.status) {
        return;
    }

    n.status = resourceObserver.promises[url] = new Promise(function (resolve, reject) {
        n.addEventListener('load', resolve);
        n.addEventListener('error', reject);
        n.addEventListener('abort', reject);
        n.addEventListener('unload', function (l) { delete resourceObserver.promises[url]} );
    });
    n.status.catch(function noop () {}); //catch reject so that it doesn't cause an exception
}

var resourceObserver = new MutationObserver(function (mutations) {
    document.querySelectorAll(resourceNodeSelector).forEach(watchResource);
});
resourceObserver.promises = {};
resourceObserver.observe(window.document, {childList: true, subtree: true});

一旦观察者就位,任何有效的资源元素都应该有一个status promise 属性,您可以随时检查它

document.querySelector('link').status.then(linkLoaded).catch(linkFailed)

一个更优雅的解决方案,不涉及使用昂贵的querySelectorAll,应该可以使用 ServiceWorker,因为它可以被编程来拦截和跟踪所有资源请求及其状态

【问题讨论】:

  • 我对样式不感兴趣。任何触发UI Events 的资源(链接、脚本、img)都将从这样的界面中受益。我提到了 ServiceWorker,因为它们可以拦截请求,所以它们可能能够处理状态并提供类似于我建议的 API
  • 我想我会删除样式表链接,因为它会分散人们的注意力
  • 不要从您的问题中删除样式表链接,而是在您的示例中添加所有不同类型的加载资源。
  • @trincot 我重组了这个问题,希望更有意义
  • 也许你可以参考这个链接,phpied.com/when-is-a-stylesheet-really-loaded

标签: javascript html dom promise


【解决方案1】:

您可以利用&lt;link&gt;元素的onloadonerror事件;见右栏Browser CSS/JS loading capabilities

创建一个对象来存储与&lt;link&gt; 元素对应的所有&lt;link&gt; 请求和解决或拒绝Promise 的状态。

onerror 事件中拒绝Promise;使用链接到Promise.reject().catch() 来处理错误,以便Promise.all() 不会停止处理作为参数传递的数组中已解决的promise。如果任何被拒绝的Promise 应停止处理承诺数组中已解决的承诺,您也可以将throw 错误从.catch() onerror 处理程序到Promise.all()

window.onload 事件处理程序中,使用Promise.all() 处理所有已解析的链接,使用在window.onload 事件之前调用的相同函数。 要等待Promise.all() 的结果可用,请将最后一个&lt;script&gt; 元素的src 设置为bundle.js .then() 链接到Promise.all()

<!DOCTYPE html>
<html>

<head>
  <title>App</title>
  <meta charset="utf-8">
  <script>
    var handleLinks = {
      links: [],
      isPending: true
    };

    function handleBeforeLoad() {
      if (document.querySelectorAll("link").length === 0) {   
        console.log("links loading state is pending..", handleLinks.isPending);
      } else {
        handleLinks.isPending = false;
        Promise.all(handleLinks.links)
          .then(function(linksContent) {
            console.log("links resolved:", linksContent
                       , "links loading state is pending.."
                       , handleLinks.isPending);
            linksContent.filter(Boolean).forEach(function(link) {
              // `content` property : html `document`,  `CSSStyleSheet` 
              // requested at `<link>` element
              console.log(link); 
            });
            // load `bundle.js`
            document.getElementById("bundle")
            .src = "bundle.js"

          })
          .catch(function(err) {
            console.log("link error:", err.message)
          })
      }
    }
    handleBeforeLoad();
    window.onload = handleBeforeLoad;

    function handleLink(el) {
      handleLinks.links.push(Promise.resolve({
        content: el.import || el.sheet,
        type: el.type,
        rel: el.rel,
        href: el.href,
        integrity: el.integrity,
        isResolved: true
      }));

    }

    function handleLinkError(el) {
      handleLinks.links.push(Promise.reject(new Error(JSON.stringify({
        error: "error loading link",
        type: el.type,
        rel: el.rel,
        href: el.href,
        integrity: el.integrity,
        isRejected: true
      }))).catch(function(err) {
        // handle error
        console.log(err);
        // this will return a resolved Promise
        return "error requesting link " + el.href;
        // `throw err` here if any rejected Promise should
        // stop `Promise.all()` from handling resolved Promise
      }));

    }
  </script>
  <link onload="handleLink(this)" 
        onerror="handleLinkError(this)" 
        rel="import" 
        href="template-bundle.html" 
        type="text/html">
  <link onload="handleLink(this)" 
        onerror="handleLinkError(this)" 
        rel="stylesheet" 
        href="bundle.css" 
        type="text/css">
  <!-- this should throw error, file does not exist -->
  <link onload="handleLink(this)" 
        onerror="handleLinkError(this)" 
        rel="stylesheet" 
        href="bundles.css" 
        type="text/css">

  <body>
    <header><img src="" alt="App logo"></header>
    <!-- Boilerplate... -->
    <script id="bundle"></script>
  </body>

</html>

plnkrhttp://plnkr.co/edit/DQj9yTDcoQJj3h7rGp95?p=preview

【讨论】:

  • 我喜欢你的想法,但内联处理程序(onload/onerror 属性)除外。这让我想到了使用 MutationObserver 来检测添加到 DOM 的链接/img/script 标签并以这种方式附加处理程序。此外,您的 beforeLoad 函数中的 document.querySelectorAll("link") 将永远不会返回任何内容,因为尚未解析 HTML 的那部分
  • “而且你的 beforeLoad 函数中的 document.querySelectorAll("link") 永远不会返回任何东西,因为 HTML 的那部分还没有被解析” 是的,那个是document 中的那个点的期望值;也就是说,为了不同的目的两次使用相同的功能。事件属性可以替换为.addEventListener(),应该会出现相同的结果。使用ServiceWorkerMutationObserver 的目的是什么?你到底想达到什么目的?
  • 我想知道链接是否加载失败,而无需我显式地将 onload/onerror 属性添加到 HTML。我还希望能够在此事件发生后获得此信息。你给了我一个使用 MutationObserver 动态添加这些处理程序的想法,这应该可以工作,但也许 ServiceWorkers 会一起消除对处理程序的需求,因为 SW 可以拦截所有请求并且可以编程为提供类似 Promise 的漂亮API
  • @Dogoku 不确定MutationObserver 如何帮助您实现目标?虽然ServiceWorker 应该返回预期的结果,而不使用事件处理程序。
  • 当一个新的 link/img/script 节点被添加到 DOM (example here) 时,MutationObserver 可以通知我。然后我可以使用addEventListener 将侦听器添加到节点,其余的与您正在做的类似
【解决方案2】:

是的,您可以在开发者工具中检查。右键单击网页并选择检查,然后窗口将打开 seleck 网络选项,您可以在那里检查状态。

【讨论】:

  • 我认为 OP 是网站检查这一点的一种方式,而不是 iser。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
相关资源
最近更新 更多