Promise는 CommonJS의 사양 중 하나입니다. 여기에는 코드 흐름을 제어하고 함수의 다층 중첩을 방지하는 데 도움이 되는 해결, 거부, 완료, 실패 등의 메서드가 있습니다. 요즘에는 웹 개발에서 비동기성이 점점 더 중요해지고 있습니다. 이러한 종류의 비선형 실행 프로그래밍은 개발자가 코드 실행 프로세스를 더 잘 제어할 수 있게 해줍니다. jQuery와 마찬가지로 모든 라이브러리는 이미 이 객체를 구현했으며, 연말에 출시될 ES6도 기본적으로 Promise를 구현합니다.
자바스크립트 디자인 패턴 실습의 프록시 모드 - 이미지 프리로딩에서는 프록시 모드를 이용하여 이미지 프리로딩 기능을 구현합니다.
이제 한 단계 더 나아가 연속된 사진을 하나씩 불러올 수 있는 기능을 완성해 보겠습니다.
기능:
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 객체는 다음과 같은 방법으로 생성됩니다
Deferred 객체는 해결 메소드를 통해 완료 이벤트를 트리거하고 완료 메소드를 사용하여 완료 이벤트에 응답합니다.
로드 성공 시 완료 이벤트입니다.
및 로딩 완료 시 응답 처리는 요소에 이미지를 설정하는 것입니다. 다음 코드는 위의 체인 작성 방법을 분해한 것입니다.
Deffered 객체는 거부 메소드를 통해 거부 이벤트를 트리거하고, 실패 메소드를 사용하여 거부 이벤트에 응답하여 로드가 실패했음을 나타냅니다.
로드 시간 초과, 종료, 예외 시 이벤트를 거부합니다.
//开始加载超时监控,超时后进行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'); });프록시 함수가 끝나면 다음 이미지 로딩을 용이하게 하기 위해 호출 위치에 대한 로딩 완료 및 실패 상태를 모니터링하는 데 사용되는 지연된 Promise 개체를 반환합니다.
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); })(); }loadOneByOne의 재귀 로딩 기능을 만듭니다.
로딩 에이전트는 먼저 내부적으로 생성됩니다. 에이전트가 이미지를 로드한 후 성공 여부에 관계없이 loadOneByOne 함수가 재귀적으로 호출되어 다음 이미지를 로드합니다.
핵심은 프록시 함수가 반환한 promise 개체에 있습니다. 로드가 완료된 후(성공 또는 실패) 다음 그림을 로드하려면 .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>