Jquery promise实现一张一张加载图片


Posted in Javascript onNovember 13, 2015

Promise是CommonJS的规范之一,拥有resolve、reject、done、fail、then等方法,能够帮助我们控制代码的流程,避免函数的多层嵌套。如今异步在web开发中越来越重要,对于开发人员来说,这种非线性执行的编程会让开发者觉得难以掌控,而Promise可以让我们更好地掌控代码的执行流程,jQuery等流行的js库都已经实现了这个对象,年底即将发布的ES6也将原生实现Promise。

在javascript设计模式实践之代理模式--图片预加载中用代理模式实现了图片预加载功能。

现在就更进一步,完成一个能够一张一张的连续图片加载的功能。

功能:

1.一张一张加载图片。

2.加载错误,超时后显示加载失败图片。

对于功能的要求,肯定会存在对加载状态事件的处理以及完成时回调函数的处理,这样不仅会造成代码上的混乱,甚至破坏各种原则,就不再用普通的方法去写了。针对这种状态通知的特点,比较合适采用promise架构进行处理,promise本质上就是订阅发布设计模式的一种,当前这个功能就用jquery自带的promise进行开发。

1.完成一个加载图片的代理创建函数,可以生成一个带有加载超时、失败、成功、取消监控能力的代理。

function createLoadImgProxy(){
  var imgCache = new Image();
  var dfd = $.Deferred();
  var timeoutTimer;
  //开始加载超时监控,超时后进行reject操作
  function beginTimeoutWatcher(){
   timeoutTimer = setTimeout(function(){
   dfd.reject('timeout');
   }, 10000);
  }
  //结束加载超时监控
  function endTimeoutWatcher(){
   if(!timeoutTimer){
   return;
   }
   clearTimeout(timeoutTimer);
  }
  //加载完成事件处理,加载完成后进行resolve操作
  imgCache.onload = function(){
   dfd.resolve(this.src);
  };
  //加载终止事件处理,终止后进行reject操作
  imgCache.onabort = function(){
   dfd.reject("aborted");
  };
  //加载异常事件处理,异常后进行reject操作
  imgCache.onerror = function(){
   dfd.reject("error");
  };
  return function(eleImg, src){
   dfd.always(function(){
   //加载完成或加载失败都要终止加载超时监控
   endTimeoutWatcher();
   }).done(function(src){
   //加载完成后,往图片元素上设置图片
   loadImg(eleImg, src);
   }).fail(function(msg){
   //加载失败后,往图片元素上设置失败图片
   loadImg(eleImg, 'loadFailed.jpg');
   });
   loadImg(eleImg, 'loading.gif');
   imgCache.src = src;
   //开始进行超时加载监控
   beginTimeoutWatcher();
   return dfd.promise();
  };
  }

 其中,通过以下的方式创建了一个Deferred对象

 

var dfd = $.Deferred();

Deferred对象通过resolve方法触发完成事件,使用done方法响应完成事件。

加载成功时的完成事件。

   imgCache.onload = function(){

                    dfd.resolve(this.src);

                };

以及加载完成时的响应处理,就是把图片设到元素上,下面的代码是上面链式写法的拆解。

 

   dfd.done(function(src){

                        //加载完成后,往图片元素上设置图片

                        loadImg(eleImg, src);

                    });

Defferred对象通过reject方法触发拒绝事件,使用fail方法响应拒绝事件,表示加载失败。

在加载超时,终止,异常时的拒绝事件。           

//开始加载超时监控,超时后进行reject操作
  function beginTimeoutWatcher(){
   timeoutTimer = setTimeout(function(){
   dfd.reject('timeout');
   }, 10000);
  }
  //加载终止事件处理,终止后进行reject操作
  imgCache.onabort = function(){
   dfd.reject("aborted");
  };
  //加载异常事件处理,异常后进行reject操作
  imgCache.onerror = function(){
   dfd.reject("error");
  };

以及加载失败时的响应处理,设置失败图片。         

dfd.fail(function(msg){
   //加载失败后,往图片元素上设置失败图片
   loadImg(eleImg, 'loadFailed.jpg');
   });

在代理函数的最后,返回deferred的promise对象,用于给调用的地方监控加载的完成和失败态,以便于下一张图片的加载。

return dfd.promise();

2.一张一张的连续加载

//一张一张的连续加载图片
  //参数:
  // srcs: 图片路径数组
  function doLoadImgs(srcs){
  var index = 0;
  (function loadOneByOne(){
   //退出条件
   if(!(s = srcs[index++])) {
   return;
   }
   var eleImg = createImgElement();
   document.getElementById('imgContainer').appendChild(eleImg);
   //创建一个加载代理函数
   var loadImgProxy = createLoadImgProxy();
   //在当前图片加载或失败后,递归调用,加载下一张
   loadImgProxy(eleImg, s).always(loadOneByOne);
  })();
  }

做一个loadOneByOne的加载递归函数。

内部先创建一个加载代理,在代理加载完图片,不管是成功还是失败后,递归调用loadOneByOne函数加载下一张图片。

关键就在于代理函数返回的promise对象,使用.always方法可在加载完成后(成功或失败)进行loadOneByOne递归调用加载下一张。

loadImgProxy(eleImg, s).always(loadOneByOne);

至此完成。

采用了promise模式后,callback函数不见了,维护状态的函数和内部变量也不见了,代码更清晰简单,使得代理函数和本地函数之间的一致性得到保护。

完整代码:

<!DOCTYPE html>
<html>
 <head>
 <meta charset="utf-8">
 </head>
 <body>
 <button id='btnLoadImg'>加载图片</button>
 <br>
 <div id='imgContainer'>
 </div>
 <br>
 <script type='text/javascript' src="./jquery-1.11.3.min.js"></script>
 <script type='text/javascript'>
  var imgSrcs = [
  'http://img.wanchezhijia.com/A/2015/3/20/17/11/de63f77c-f74f-413a-951b-5390101a7d74.jpg',
  'http://www.newbridgemotorsport.com/files/6413/9945/0406/IMG_3630.jpg',
  'http://www.carsceneuk.com/wp-content/uploads/2015/03/88y9989.jpg',
  'http://mfiles.sohu.com/20130223/5ff_403b2e7a_7a1f_7f24_66eb_79e3f27d58cf_1.jpg',
  'http://img1.imgtn.bdimg.com/it/u=2678963350,1378052193&fm=21&gp=0.jpg'
  ];
  $(document).ready(function(){
  $('#btnLoadImg').bind('click', function(){
   doLoadImgs(imgSrcs);
  });
  });
  //创建img标签
  //这里用自执行函数加一个闭包,是为了可以创建多个id不同的img标签。
  var createImgElement = (function(){
  var index = 0;
  return function() {
   var eleImg = document.createElement('img');
   eleImg.setAttribute('width', '200');
   eleImg.setAttribute('heght', '150');
   eleImg.setAttribute('id', 'img' + index++);
   return eleImg;
  };
  })();
  function loadImg(img, src) {
  img.src = src;
  }
  function createLoadImgProxy(){
  var imgCache = new Image();
  var dfd = $.Deferred();
  var timeoutTimer;
  //开始加载超时监控,超时后进行reject操作
  function beginTimeoutWatcher(){
   timeoutTimer = setTimeout(function(){
   dfd.reject('timeout');
   }, 10000);
  }
  //结束加载超时监控
  function endTimeoutWatcher(){
   if(!timeoutTimer){
   return;
   }
   clearTimeout(timeoutTimer);
  }
  //加载完成事件处理,加载完成后进行resolve操作
  imgCache.onload = function(){
   dfd.resolve(this.src);
  };
  //加载终止事件处理,终止后进行reject操作
  imgCache.onabort = function(){
   dfd.reject("aborted");
  };
  //加载异常事件处理,异常后进行reject操作
  imgCache.onerror = function(){
   dfd.reject("error");
  };
  return function(eleImg, src){
   dfd.always(function(){
//   alert('always end');
   //加载完成或加载失败都要终止加载超时监控
   endTimeoutWatcher();
   }).done(function(src){
//   alert('done end');
   //加载完成后,往图片元素上设置图片
   loadImg(eleImg, src);
   }).fail(function(msg){
//   alert('fail end:' + msg);
   //加载失败后,往图片元素上设置失败图片
   loadImg(eleImg, 'loadFailed.jpg');
   });
   loadImg(eleImg, 'loading.gif');
   imgCache.src = src;
   //开始进行超时加载监控
   beginTimeoutWatcher();
   return dfd.promise();
  };
  }
  //一张一张的连续加载图片
  //参数:
  // srcs: 图片路径数组
  function doLoadImgs(srcs){
  var index = 0;
  (function loadOneByOne(){
   //退出条件
   if(!(s = srcs[index++])) {
   return;
   }
   var eleImg = createImgElement();
   document.getElementById('imgContainer').appendChild(eleImg);
   //创建一个加载代理函数
   var loadImgProxy = createLoadImgProxy();
   //在当前图片加载或失败后,递归调用,加载下一张
   loadImgProxy(eleImg, s).always(loadOneByOne);
  })();
  }
 </script>
 </body>
</html>
Javascript 相关文章推荐
解析window.open的使用方法总结
Jun 19 Javascript
jquery实现盒子下拉效果示例代码
Sep 12 Javascript
js动态添加事件并可传参数示例代码
Oct 21 Javascript
JavaScript表单通过正则表达式验证电话号码
Mar 14 Javascript
JS输入用户名自动显示邮箱后缀列表的方法
Jan 27 Javascript
js控制网页前进和后退的方法
Jun 08 Javascript
基于JS实现的笛卡尔乘积之商品发布
May 13 Javascript
js检测离开或刷新页面时表单数据是否更改的方法
Aug 02 Javascript
微信小程序开发实战教程之手势解锁
Nov 18 Javascript
AngularJS实现表格的增删改查(仅限前端)
Jul 04 Javascript
微信小程序登录按钮遮罩浮层效果的实现方法
Dec 16 Javascript
深入了解Vue3模板编译原理
Nov 19 Vue.js
jquery转盘抽奖功能实现
Nov 13 #Javascript
javascript生成随机数方法汇总
Nov 12 #Javascript
js正则表达式验证邮件地址
Nov 12 #Javascript
每天一篇javascript学习小结(Boolean对象)
Nov 12 #Javascript
为何JS操作的href都是javascript:void(0);呢
Nov 12 #Javascript
基于jquery实现左右按钮点击的图片切换效果
Jan 27 #Javascript
js图片轮播效果实现代码
Apr 18 #Javascript
You might like
综合图片计数器
2006/10/09 PHP
浅析虚拟主机服务器php fsockopen函数被禁用的解决办法
2013/08/07 PHP
php中header设置常见文件类型的content-type
2015/06/23 PHP
解读PHP中的垃圾回收机制
2015/08/10 PHP
phpstorm最新激活码分享亲测phpstorm2020.2.3版可用
2020/11/22 PHP
js中页面的重新加载(当前页面/上级页面)及frame或iframe元素引用介绍
2013/01/24 Javascript
javascript级联下拉列表实例代码(自写)
2013/05/10 Javascript
JavaScript判断变量是对象还是数组的方法
2014/08/28 Javascript
JavaScript调试工具汇总
2014/12/23 Javascript
JS中的Replace方法使用经验分享
2015/05/20 Javascript
详解AngularJS中的http拦截
2016/02/09 Javascript
JSONP和批量操作功能的实现方法
2016/08/21 Javascript
Vue.js每天必学之计算属性computed与$watch
2016/09/05 Javascript
简单谈谈Javascript函数中的arguments
2017/02/09 Javascript
基于cookie实现zTree树刷新后展开状态不变
2017/02/28 Javascript
Vue2.0 事件的广播与接收(观察者模式)
2018/03/14 Javascript
vue中子组件的methods中获取到props中的值方法
2018/08/27 Javascript
Vuex,iView UI面包屑导航使用扩展详解
2019/11/04 Javascript
Vue开发中常见的套路和技巧总结
2020/11/24 Vue.js
复制粘贴功能的Python程序
2008/04/04 Python
浅谈python对象数据的读写权限
2016/09/12 Python
python常见排序算法基础教程
2017/04/13 Python
Python3 加密(hashlib和hmac)模块的实现
2017/11/23 Python
python实现俄罗斯方块
2018/06/26 Python
对Python 检查文件名是否规范的实例详解
2019/06/10 Python
python变量的存储原理详解
2019/07/10 Python
python中tkinter窗口位置\坐标\大小等实现示例
2020/07/09 Python
Python try except else使用详解
2021/01/12 Python
加拿大女鞋品牌:ALDO
2016/11/13 全球购物
英国第一摩托车和摩托车越野配件商店:GhostBikes
2019/03/10 全球购物
药学专业个人自我评价
2013/11/11 职场文书
工作自荐信
2013/12/11 职场文书
职业生涯规划书基本格式
2014/01/06 职场文书
充分就业社区汇报材料
2014/05/07 职场文书
2014年银行工作总结范文
2014/11/12 职场文书
一年级下册数学教学反思
2016/02/16 职场文书