向dom添加大量img元素时出现net::ERR_INSUFFICIENT_RESOURCES错误

53

我在一个图片较多的网站上使用jquery和backbone.js。该网站的核心功能涉及许多相对较小的图片(150x180像素的jpg文件)。图片列表通过ajax/json使用backbone.js集合获取。然后针对集合中的每个模型都会呈现包含img元素的视图,然后将视图添加到dom中。

有一个用户具有数千张图片-与我们大多数普通用户拥有的图片数量相比是一个超级边缘案例。当这位用户的图像数据加载时,浏览器无法处理所有图片,至少不能按照当前代码的方式进行处理。大约一半的图像最终可以正常加载,但浏览器(我正在使用Chrome 35)会不响应几分钟。另一半图像无法加载,并且浏览器控制台显示未加载的图像的“net::ERR_INSUFFICIENT_RESOURCES”错误。

以下是我们加载图像的关键代码部分。是否有人能够提供关于为什么出现此图像加载失败的技术解释,并提供一个不需要向图像列表添加分页或“点击这里加载更多”功能的解决方案?

// inside the view that renders the images
render: function () {
    this.collection.each(this.addOne, this);    
    return this;
},
addOne: function (imgModel) {
    var imgView = new App.Views.ImageView({ model: imgModel});
    this.$el.append(imgView.render().el);
}

这是 App.View.ImageView 视图的 render() 代码:

render: function () {
    var renderedTemplate= theTemplate(this.model.toJSON());
    this.$el.html(renderedTemplate);
    return this;
}

App.View.ImageView使用的模板(只会通过_.template编译一次):

<script type="text/template" id="thumb-template">          
        <a href="<%= ImageUrl%>"><img src="<%= ImageUrl%>" /></a>
        <div class="delete"></div>
</script>

8
结果表明,尽管加载如此多的图像非常缓慢,但奇怪的错误来自于AdBlock Plus。我禁用了这个Chrome插件,错误消失了,图片也能正常加载......只是浏览器需要在幕后完成所有必要的工作,渲染页面并执行所有操作,这需要花费很长时间。 - Rafe
我知道你说你不想要一个"点击这里加载更多"的解决方案,但是我有同样的情况,我认为无限滚动可能是最好的选择。这也是合理的。为什么要强制浏览器一次渲染8k张图片,而用户一次只能看到大约20张呢? - djKianoosh
在我的情况下,这些图像是使用Isotope平铺和视觉过滤的。如果客户端没有加载,您无法对其进行排序/过滤,因此我需要加载所有项目。 - Rafe
2
啊,我明白了。在我的情况下,我会对元数据进行排序/过滤,并且显示图像与此分离。但是我不知道Isotope是否支持这种操作。 - djKianoosh
2
广告拦截加评论也帮助我解决了问题。 - rgshenoy
2个回答

47

我认为这是影响你的错误:https://bugs.chromium.org/p/chromium/issues/detail?id=108055

从2011年到2016年一直有讨论,并且仍在进行中。基本上,Chrome无法处理短时间内大量请求。

以下是对我的应用程序 稍微有帮助 的方法:

  • 您可以添加事件处理程序,如img.addEventListener("error",tryAgainLater),但这不会解决其他无法加载资源的问题,因此加载数百张图片的脚本可能会干扰其他操作。
  • 尽量缓存更多图像以减少网络请求的数量。
  • 改用Firefox...显然不能告诉客户。

以下是 没有起作用 的方法:

  • 将图像合成到画布上并丢弃单个图像。这没有帮助,因为它与网络请求相关,而不是存储在内存中的图像。
  • 不要在前一个图像完全加载之前开始下一个请求。也许这是因为连接实际上需要时间才能关闭或从内存(或其他资源)中删除。

尚未尝试:

  • 通过HTTP/2或SPDY加载图像,其中有许多请求但仅一个连接。
  • 在您的情况下,你可以将图片内联以避免发出请求。 以下是来自https://css-tricks.com/data-uris/ 的示例:<img width="16" height="16" alt="star" src="data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" />

  • 1
    嗨,FYI我尝试了“在有许多请求但只有一个连接的情况下通过HTTP/2加载图像”,但结果仍然是同样的问题。我的测试HTML尝试加载10000个25x25像素的图像。 - Rolf
    同意 - 我们也尝试了HTTP/2,但没有起到帮助的作用。 - Meekohi
    1
    最简单的解决方案是减少请求次数,通过在每个请求中发送更多的数据。例如,可以在服务器上压缩多个图像文件,并在客户端上解压缩它们。当我在Dropzone中分块发送大文件时遇到了这个错误,通过增加块大小,我能够降低请求次数并避免ERR_INSUFFICIENT_RESOURCES错误。 - Helge Schneider
    截至2018年中,该错误已经被修复。 - Vessel
    非常感谢您的详细答复。对于我来说,在第一个错误后停止后续请求,稍后再尝试并设置超时时间来处理。这是一种很拼凑的解决方案,但对于快速修复很有效(在我的情况下仅涉及大量导出数据而不是图像,因此延迟加载确实是解决方案)。 - Giwrgos Lampadaridis
    在Node.js中使用多个线程和worker_threads怎么样? - undefined

    5

    对于浏览器来说,toJSON()方法是非常昂贵的,因为它会克隆模型中的“属性”以返回JSON表示。

    ...
    // Return a copy of the model's `attributes` object.
    toJSON: function(options) {
      return _.clone(this.attributes);
    },
    ...
    

    在某些情况下,当我只想显示我的模型信息时,我直接使用“attributes”属性,这样可以节省处理时间。
    请尝试将ImageView文件中的此行替换:
    theTemplate(this.model.toJSON());
    

    对于

    theTemplate(this.model.attributes);
    

    希望这些信息能够对您有所帮助。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接