不需要任何依赖的图片加载错误处理的工具类load-image.js

针对微博舆情分析系统中境外图片加载失败的问题,本文详细介绍了如何实现图片加载的错误重试机制,确保图片能够成功加载,提升用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求的诞生:

先简单介绍一下业务场景,我们的项目是一个微博舆情分析系统,可以根据用户设置的关键字监测相关微博舆情,并进行实时推送。监测范围涵盖境内和境外微博平台(境内:新浪、腾讯,境外:twitter)。

因为访问境外的微博需要经过代理转发,导致经常有图片加载不出来,严重影响用户观感。此时就需要一个错误重试的处理,不要让界面上出现这个图片加载错误的图标,如下所示:

思路:

通过外部传进来的代表图片来源的参数,判断是否是境外图片。如果是,则先显示一张默认图片,等图片加载成功后再替换默认图片。如果图片加载错误了,间隔一段时间进行重试,直到加载成功,或失败次数大于设定的最大阈值时停止。

预备知识:

图片加载主要有三种状态:成功、失败、加载中(pendding),这个可以在控制台的NetWork里面看到。

Image对象有一个属性complete,可返回浏览器是否已完成对图像的加载。complete为true表示图片已经加载完成,为false则有两种情况,一是加载失败,而是正在加载中还没有结果。

开始撸:

先写一个函数,里面定义图片加载失败后的最大重试次数maxCount,初始化错误重试次数,并对图片url进行一个非空验证

/**
 * @param imageUrl  {string} 图片URL
 * @param callBack {function} 图片加载成功后的回调(参数:{imageUrl})
 */
function loadImage(imageUrl, callBack) {
  if (!imageUrl) {
    return callBack({
      imageUrl: ''
    });
  }
  const maxCount = 5; // 最大重试次数
  let count = 0;      // 重试次数
  
  // 验证图片是否加载成功
  validateImage(imageUrl, callBack, maxCount, count);
}
复制代码

接下来就是核心代码validateImage函数的实现了, 首先定义好函数内部的变量

function validateImage(imageUrl, callBack, maxCount, count) {
  var img = new Image(),   // 创建一个image对象
    timeId,
    timer,
    finished,              // 图片加载成功标志位
    time = 40,             // 观察图片状态的定时器执行间隔
    timeCount = 0,         // 观察图片状态的定时器执行次数
    totalTime = 0,         // 观察图片状态的定时器累计执行时间
    errTimeout = 1000;     // 失败重试的延时
复制代码

然后给img.src赋值,此时图片就开始加载了

  img.src = imageUrl;
  count++;
复制代码

img.complete判断图片是否加载完毕,为true,则表示图片已加载成功,直接执行回调函将正确的图片路径返回

  if (img.complete) {
    callBack({
      imageUrl: imageUrl,
    });
  }
复制代码

如果img.complete为false,则需要分情况处理。

情况一:图片加载中(pendding状态),我们需要定时去看图片状态,直到图片加载成功或者失败,用onload事件或onerror事件去捕获。
然而这个地方有个大坑,什么坑呢?
图片有可能一直处于pendding状态,此时是不会有任何返回值的,所以没有办法用onloadonerr事件捕获,定时器一直重复执行,内存狂飙!

    else {
        if(totalTime < 60000){
          timeId = setTimeout(function () {
            timer();
            timeCount++;
            // 每次执行间隔是上次执行间隔的两倍
            time = 40*Math.pow(2,timeCount);
            totalTime += time;
          }, time);
        }else{
          clearTimeout(timeId);
        }
      }
    };
    // 执行观察定时器
    timeId = setTimeout(function () {
      timer();
      totalTime += time;
    }, time);
复制代码

此时如果图片加载成功或失败则会被onload捕获或onerr捕获。图片加载成功进入到onload里面,执行回调并将正确的图片路径返回;图片加载失败,进入到onerror里面,并进行重试,直到成功或者重试次数大于maxCount结束。

    img.onload = function () {
      clearTimeout(timeId);
      if (!finished) {
        return callBack({
          imageUrl: imageUrl
        });
      }
    };
    img.onerror = function () {
      if (count < maxCount) {
        clearTimeout(timeId);
        // 错误重试间隔每次以2s递增
        errTimeout += (count-1) * 2000;
        // 递归
        timeId = setTimeout(function () {
          validateImage(imageUrl, callBack, maxCount, count);
        }, errTimeout);
      } else {
        clearTimeout(timeId);
        if (!finished) {
          callBack({
            imageUrl: ''
          });
        }
      }
    };
  }
复制代码

ok,大功告成!

写的不好,仅供参考。谢谢大家

源码:

/**
 * 验证图片是否加载成功
 * @param imageUrl
 * @param callBack
 * @param maxCount {string} 最大轮询次数
 * @param count {string} 重新加载次数
 */
function validateImage(imageUrl, callBack, maxCount, count) {
  let img = new Image(), timeId, timer, finished, time = 40, timeCount = 0, totalTime = 0, errTimeout = 1000;
  img.src = imageUrl;
  count++;
  if (img.complete) {
    callBack({
      imageUrl: imageUrl,
    });
  } else {
    timer = function () {
      if (img.width > 0 || img.height > 0) {
        finished = true;
        callBack({
          imageUrl: imageUrl
        });
      } else {
        if(totalTime < 60000){
          timeId = setTimeout(function () {
            timer();
            timeCount++;
            time = 40*Math.pow(2,timeCount);
            totalTime += time;
          }, time);
        }else{
          clearTimeout(timeId);
        }
      }
    };
    timeId = setTimeout(function () {
      timer();
      totalTime += time;
    }, time);
    img.onload = function () {
      clearTimeout(timeId);
      if (!finished) {
        return callBack({
          imageUrl: imageUrl
        });
      }
    };
    img.onerror = function () {
      if (count < maxCount) {
        clearTimeout(timeId);
        errTimeout += (count-1) * 2000;
        timeId = setTimeout(function () {
          validateImage(imageUrl, callBack, maxCount, count);
        }, errTimeout);
      } else {
        clearTimeout(timeId);
        if (!finished) {
          callBack({
            imageUrl: ''
          });
        }
      }
    };
  }
}

/**
 * @param imageUrl  {string} 图片URL
 * @param callBack {function} 图片加载成功后的回调(参数:{imageUrl})
 * @returns {*}
 */
function loadImage(imageUrl, callBack) {
  if (!imageUrl) {
    return callBack({
      imageUrl: ''
    });
  }
  const maxCount = 5;
  let count = 0;

  validateImage(imageUrl, callBack, maxCount, count);
}
export default loadImage;

复制代码

转载于:https://juejin.im/post/5c5530cce51d457fff40fb3b

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值