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 相关文章推荐
获取鼠标在div中的相对位置的实现代码
Dec 30 Javascript
jQuery实现返回顶部功能适合不支持js的浏览器
Aug 19 Javascript
node.js操作mongoDB数据库示例分享
Nov 26 Javascript
jQuery实现鼠标滑向当前图片高亮显示并且其它图片变灰的方法
Jul 27 Javascript
JavaScript动态创建div等元素实例讲解
Jan 06 Javascript
node模块机制与异步处理详解
Mar 13 Javascript
JavaScript代码性能优化总结(推荐)
May 16 Javascript
JavaScript判断用户名和密码不能为空的实现代码
May 16 Javascript
angularjs 源码解析之scope
Aug 22 Javascript
jquery获取table指定行和列的数据方法(当前选中行、列)
Nov 07 Javascript
js 取消页面可以选中文字的功能方法
Jan 02 Javascript
webpack4 从零学习常用配置(小结)
May 28 Javascript
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
CakePHP去除默认显示的标题及图标的方法
2008/10/22 PHP
PHP获取MySql新增记录ID值的3种方法
2014/06/24 PHP
Yii2中使用asset压缩js,css文件的方法
2016/11/24 PHP
理解Javascript_12_执行模型浅析
2010/10/18 Javascript
js将字符串转成正则表达式的实现方法
2013/11/13 Javascript
鼠标移到div,浮层显示明细,弹出层与div的上边距左边距重合(示例代码)
2013/12/14 Javascript
instanceof和typeof运算符的区别详解
2014/01/06 Javascript
JS辨别访问浏览器判断是android还是ios系统
2014/08/19 Javascript
node.js中Socket.IO的进阶使用技巧
2014/11/04 Javascript
不使用ajax实现无刷新提交表单
2014/12/21 Javascript
项目中常用的JS方法整理
2015/01/30 Javascript
tuzhu_req.js 实现仿百度图片首页效果
2015/08/11 Javascript
网页从弹窗页面单选框传值至父页面代码分享
2015/09/29 Javascript
学习JavaScript图片预加载模块
2016/11/07 Javascript
js实现网页定位导航功能
2017/03/07 Javascript
js实现点击切换checkbox背景图片的简单实例
2017/05/08 Javascript
React Native实现进度条弹框的示例代码
2017/07/17 Javascript
jquery实现企业定位式导航效果
2018/01/01 jQuery
详解ES7 Decorator 入门解析
2019/02/18 Javascript
在antd中setFieldsValue和defaultVal的用法
2020/10/29 Javascript
[50:58]2018DOTA2亚洲邀请赛 4.1 小组赛 B组 Mineski vs EG
2018/04/03 DOTA
简单介绍Python的轻便web框架Bottle
2015/04/08 Python
Python中的anydbm模版和shelve模版使用指南
2015/07/09 Python
python 网络编程常用代码段
2016/08/28 Python
python制作小说爬虫实录
2017/08/14 Python
Python打印“菱形”星号代码方法
2018/02/05 Python
Python爬虫使用Selenium+PhantomJS抓取Ajax和动态HTML内容
2018/02/23 Python
Python import与from import使用及区别介绍
2018/09/06 Python
python 两个数据库postgresql对比
2019/10/21 Python
Python与C/C++的相互调用案例
2021/03/04 Python
2019年Java面试必问之经典试题
2012/09/12 面试题
司法局火灾防控方案
2014/06/05 职场文书
农村党支部书记四风问题个人对照检查材料
2014/09/21 职场文书
教师群众路线教育实践活动个人对照检查材料
2014/11/04 职场文书
财务会计实训报告
2014/11/05 职场文书
2015年挂职锻炼个人总结
2015/10/22 职场文书