有没有办法知道链接/脚本是否仍然挂起或失败 [英] Is there a way to know if a link/script is still pending or has it failed

查看:116
本文介绍了有没有办法知道链接/脚本是否仍然挂起或失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从下面的html中知道,如果链接[rel = import] 链接[rel = stylesheet] img 脚本挂起/加载/失败/中止事件发生之后的任何时候都需要添加听众。

 <!DOCTYPE html> 
< html>
< head>
< title> App< / title>
< meta charset =utf-8>
< link rel =importhref =template-bundle.html>
< link rel =stylesheethref =bundle.css>
< / head>
< body>
< header>< img src =logo.pngalt =App logo>< / header>
<! - 锅炉... - >
< script src =./ app-bundle.js>< / script>
< / body>
< / html>

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






奖金问题:这可以使用服务工作者实现吗?



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




    <如果请求仍在等待,则li>正在等待
  • 如果加载事件触发

  • 如果错误中止已被触发,则被拒绝。



感谢您的帮助






更新&解决方案:



感谢@pritishvaidya和@ guest271314的答案,我能够使用 MutationObserver ,涉及观看DOM以添加资源节点(link,img,脚本),并向他们添加一个可以解决的承诺,如上所述



这是非常好的,脚本标签需要唯一的caviat在任何其他资源之前的< head> 中内联。 以下是一个示例

  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,这样它不会引起异常
}

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

一旦观察者到位,任何有效的资源元素应该有一个状态承诺属性,您可以在任何时间点检查

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

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

解决方案

您可以使用 onload onerror < link> 元素;请参阅右栏中的浏览器CSS / JS加载功能



创建一个对象来存储所有< link> 请求的状态,并解决或拒绝 Promise 对应于< link> 元素。



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

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

 <!DOCTYPE html> ; 
< html>

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

函数handleBeforeLoad(){
if(document.querySelectorAll(link)。length === 0){
console.log(links load state is pending。 ,handleLinks.isPending);
} else {
handleLinks.isPending = false;
Promise.all(handleLinks.links)
.then(function(linksContent){
console.log(links resolved ...,linksContent
,链接加载状态正在等待
,handleLinks.isPending);
linksContent.filter(Boolean).forEach(function(link){
//`content` property:html`document`,'CSSStyleSheet'
//在`< link>`元素
console.log(link);
});
//加载`bundle.js`
文档.getElementById(bundle)
.src =bundle.js

})
.catch(function(err){
console.log(链接错误:,err.message)
})
}
}
handleBeforeLoad();
window.onload = handleBeforeLoad;

函数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
}));

}

函数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,
)被拒绝:true
})))。catch(function(err){
// handle error
console.log(err);
//这将返回一个已解决的承诺
返回错误请求链接+ el.href;
//`throw err`这里如果任何拒绝的承诺应该
//停止`Promise.all()`从处理解决的承诺
}));

}
< / 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>
<! - 这应该抛出错误,文件不存在 - >
< link onload =handleLink(this)
onerror =handleLinkError(this)
rel =stylesheet
href =bundles.css
type =text / css>

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

< / html>

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


I'd like to know from the html below, if link[rel=import], link[rel=stylesheet], img and script are pending/loaded/failed/aborted without the need to add listeners beforehand and at any point after the event has happened

<!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>

In other words: Is there an interface which provides something similar to a Bluebird's isPending(), isResolved(), isRejected() methods or a regular ES6 Promise?


Bonus question: Is this something can be achieved using a Service Worker?

Since SW can intercept requests and know their status, I was wondering if I can implement an API which returns a Promise that

  • is pending if request is still pending
  • is resolved if load event fired
  • is rejected if error or aborted was fired.

Thanks for the help


Update & Solution:

Thanks to the answers of @pritishvaidya and @guest271314, I was able to come up with a viable solution using MutationObserver that involves watching DOM for additions of resource nodes (link,img,script) and adding a promise to them that will resolve as described above

This works great, with the only caviat that the script tag needs to be inlined in <head> before any other resource. 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});

Once the observer is in place, any valid resource element, should have a status promise property that you can check at any point in time

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

A more elegant solution, that does not involve using the expensive querySelectorAll, should be possible with ServiceWorker, since it can be programmed to intercept and keep track of all resource requests and their status

解决方案

You can utilize onload, onerror events of <link> element; see Browser CSS/JS loading capabilities at right column.

Create an object to store status of all <link> requests and resolved or rejected Promise corresponding to the <link> element.

Reject Promise at onerror event; use .catch() chained to Promise.reject() to handle error so that Promise.all() will not stop processing resolved promises within array passed as parameter. You can also throw error from .catch() at onerror handler to Promise.all() if any rejected Promise should stop processing of resolved promise within array of promises.

At window.onload event handler, use Promise.all() to process all resolved links, using same function called before window.onload event. To wait for results of Promise.all() to be available, set src of last <script> element to bundle.js at .then() chained to 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>

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

这篇关于有没有办法知道链接/脚本是否仍然挂起或失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆