[js异步编程]jq异步编程中deferred的promise

更新时间:2020-03-23    来源:python    手机版     字体:

【www.bbyears.com--python】

异步操作后的逻辑由回调函数来执行,回调函数(callback function)顾名思义就是“回头调用的函数”,函数体事先已定义好,在未来的某个时候由某个事件触发调用,而这个时机,是程序本身无法控制的。

举几个常见例子:

事件绑定


动画


Ajax


上面的例子简单、典型,易于阅读和理解。

为了引出本文的主题,假设现在有3个ajax异步操作,分别为A、B、C,每个都封装成了函数,并可传入success回调作为参数。

请考虑以下场景:

希望这3个异步操作按顺序执行,形成执行队列,即A执行完了,B执行;B执行完了,C执行;

希望A,B完成了,再执行C;

对于场景(1),代码大概会是这样:


按传统的写法,自然而然会形成回调函数的多层嵌套,如果代码量比较多的话,代码的可读性和维护性会比较差。

对于场景(2),代码可能要复杂些了:


a) A、B操作分别需要有状态变量isADone,isBDone,默认值均为false;A成功后isADone置为true,B成功后isBDone置为true;

b) 需要一段代码来反复探测上述2个状态变量,我们把这个行为称为pending,等pending到2个状态都为true时则执行C,并终止pending;

就本场景而言,上述方案还可勉强接受,如果情况再复杂些,代码的可读性和维护性也会大打折扣。

以上2种场景,只限定了3个异步操作的执行关系,实际的开发场景可能要比这个复杂的多,为了开发者能够更优雅的处理js异步操作,jq引入了Deferred对象(1.Deferred),我们先来了解下Deferred对象,然后看看它能为我们的异步编程带来哪些益处。


Deferred特性介绍

先创建一个Deferred实例。


new关键字是可选的,可以不写,这是因为在Deferred构造函数中处理了,但不代表用原生的js基于构造函数创建对象不用写new。

deferred带有3种状态:pending(待定)、resolved(成功)、rejected(失败)。

deferred的状态可以通过api进行切换,但不可逆。


可从英文字面意思理解:resolve(解决)后成功,reject(拒绝)后失败。

状态不可逆,是指一旦从待定状态切换到任何一个确定状态后,再次调用resolve或reject对原状态将不起任何作用。

deferred通过语义对应的api来绑定不同状态时执行的回调函数。


resolve: done, always

reject: fail, always

其中always绑定的回调函数,不论deferred的状态是成功或失败,总会执行。

deferred还有一个then方法,来简化done、fail的写法:


另外,值得注意的是,当deferred状态切换后(调用reject或者resolve后),再进行回调函数的绑定,那么对应状态的回调函数会立即执行。

举个例子:


deferred还可以返回自己的操作子集。


promise只有绑定回调的api,而没有状态切换的api。

deferred: reject, resolve, done, fail, then, always

promise: done, fail, then, always

很明显promise是用来开放给外部调用的,而deferred通常用于模块内部,来控制自身状态的切换。

举个直观的例子,一般在js中我们会用setTimeout来做延时执行,如:


我们用deferred来封装下,代码如下:


Wait函数内部,通过deferred来进行状态切换,返回promise对象,这样就可以在wait外部进行回调函数的绑定。

其实该原理在jq中有个非常典型的案例,那就是$.ajax方法,该方法会返回一个promise,而方法内部有个deferred用于决定自身状态,所以相对传统的ajax写法,我们也可以这么写:


其中done,fail等方法还可以接受多个callback、或者以callback array作为参数。

假定有个ajax请求,成功后需要对返回结果进行3个独立的处理,分别对应3个函数,我们可以这么写:


这样获取数据的逻辑和处理数据的逻辑进行了分离,数据处理不会全都堆在success回调函数中,代码整体看起来就更简洁易读。


回到之前的问题

再了解了Deferred特性和简单应用后,我们再回头考虑前面提到的2个场景,是否能够用更好的方案来实现呢?答案是肯定的。

我们先看场景(2):A、B完成了,再执行C。

为什么先看场景(2),因为jq中正好有个现成的api:$.when,能够很方便的满足该需求。

$.when可接受多个deferred(promise)对象,$.when会返回一个promise,用作后续回调的绑定,官网示例如下:


当$.when中2个异步请求均返回成功后,即会执行myFunc回调;

当$.when中只要有一个异步请求失败,即会执行myFailure回调;

解决方案已经很清晰了,我们稍加改造之前的asyncA、asyncB方法,让它们分别返回各自的promise,然后直接使用$.when即可。

我可以先用ajax来做代码示例:


下面再来个直接使用deferred的代码实例:


整体上,是否感觉代码更清晰,更利于理解了呢?

我们再来看场景(1):A执行完了,B执行;B执行完了,C执行。

要完成这个实现的改造,需要深入了解下deferred对象的then方法。上面我们介绍了then的一般用法:


用于done,fail的简化写法。但它还有一个非常重要的作用,可以用来传递deferred(promise)对象,实现“任务链条”(chain tasks)。

下面就用then来实现场景(1)的需求:


是不是感觉很简单,如果C后面还有D,那继续往下then就可以了。


假定asyncA是一个ajax操作,其中回调函数data参数,即为ajax成功后,后台返回的数据。

值得注意的是,如果用then方法进行deferred对象的传递,回调函数必须return一个deferred。

上述的例子还有一个特点:由于then的第一个参数是deferred对象成功时执行的回调,若deferred状态切换到失败,则后续then的成功回调将不再执行,“任务链”就中断了。

该场景有一定的实践价值,比如一个业务网站,页面上有多个展示模块都是通过ajax问后台拿数据的,如果页面一进来,同时向后台发好几个ajax请求,会瞬间增加后台IO的压力,可能会增加用户等待界面反馈数据的时间,造成体验下降。在这种情况下,把ajax请求作为队列处理是比较合适的,可按重要性逐步请求获取数据,提高性能和渲染体验。


小结

本文只是初步的介绍了下jq中的Deferred,并没有深入到每一个细节,但它的基本功用应该是覆盖到了,感兴趣的同学可以前往jq官网,自行研究下。

其实promise就可以按照字面理解为“承诺”,可以把deferred理解成你的一个手下,你让他去跟进一件事,而这个事情什么时候可以有个结果,你并不清楚,但他会给你承诺,将按照你的意图:事情若这样,后续要做什么;事情若那样,后续要做什么。


使用 jQuery Deferred 和 Promise 创建响应式应用程序

抽象来说,deferreds 可以理解为表示需要长时间才能完成的耗时操作的一种方式,相比于阻塞式函数它们是异步的,而不是阻塞应用程序等待其完成然后返回结果。deferred对 象会立即返回,然后你可以把回调函数绑定到deferred对象上,它们会在异步处理完成后被调用。


Promise

  你可能已经阅读过一些关于promise和deferreds实现细节的资料。在本章节中,我们大致介绍下promise如何工作,这些在几乎所有的支持deferreds的javascript框架中都是适用的。

  一般情况下,promise作为一个模型,提供了一个在软件工程中描述延时(或将来)概念的解决方案。它背后的思想我们已经介绍过:不是执行一个方法然后阻塞应用程序等待结果返回,而是返回一个promise对象来满足未来值。

  举一个例子会有助于理解,假设你正在建设一个web应用程序, 它很大程度上依赖第三方api的数据。那么就会面临一个共同的问题:我们无法获悉一个API响应的延迟时间,应用程序的其他部分可能会被阻塞,直到它返回 结果。Deferreds 对这个问题提供了一个更好的解决方案,它是非阻塞的,并且与代码完全解耦 。

  Promise/A提议’定义了一个’then‘方法来注册回调,当处理函数返回结果时回调会执行。它返回一个promise的伪代码看起来是这样的:

    promise = callToAPI( arg1, arg2, ...);   
    promise.then(function( futureValue ) {   
    /* handle futureValue */   
    });   
    promise.then(function( futureValue ) {  
     /* do something else */  
     });  

  此外,promise回调会在处于以下两种不同的状态下执行:

    resolved:在这种情况下,数据是可用
    rejected:在这种情况下,出现了错误,没有可用的值

  幸运的是,'then'方法接受两个参数:一个用于promise得到了解决(resolved),另一个用于promise拒绝(rejected)。让我们回到伪代码:

    promise.then( function( futureValue ) {   
    /* we got a value */   
    } , function() {   
    /* something went wrong */  
     } );  

  在某些情况下,我们需要获得多个返回结果后,再继续执行应用程序(例如,在用户可以选择他们感兴趣的选项前,显示一组动态的选项)。这种情况下,'when'方法可以用来解决所有的promise都满足后才能继续执行的场景。

    when(   
      promise1,   
      promise2,  
       ...  
     ).then(function( futureValue1, futureValue2, ... ) {   
      /* all promises have completed and are resolved */  
     });  

  一个很好的例子是这样一个场景,你可能同时有多个正在运行的动画。 如果不跟踪每个动画执行完成后的回调,很难做到在动画完成后执行下一步任务。然而使用promise和‘when’方式却可以很直截了当的表示: 一旦动画执行完成,就可以执行下一步任务。最终的结果是我们可以可以简单的用一个回调来解决多个动画执行结果的等待问题。 例如:

    when( function(){   
    /* animation 1 */   
    /* return promise 1 */  
    }, function(){  
     /* animation 2 */   
    /* return promise 2 */   
    } ).then(function(){  
     /* once both animations have completed we can then run our additional logic */   
    });  

  这意味着,基本上可以用非阻塞的逻辑方式编写代码并异步执行。 而不是直接将回调传递给函数,这可能会导致紧耦合的接口,通过promise模式可以很容易区分同步和异步的概念。

   在下一节中,我们将着眼于jQuery实现的deferreds,你可能会发现它明显比现在所看到的promise模式要简单。


jQuery的Deferreds

  jQuery在1.5版本中首次引入了deferreds。它 所实现的方法与我们之前描述的抽象的概念没有大的差别。原则上,你获得了在未来某个时候得到‘延时’返回值的能力。在此之前是无法单独使用的。 Deferreds 作为对ajax模块较大重写的一部分添加进来,它遵循了CommonJS的promise/ A设计。1.5和先前的版本包含deferred功能,可以使$.ajax() 接收调用完成及请求出错的回调,但却存在严重的耦合。开发人员通常会使用其他库或工具包来处理延迟任务。新版本的jQuery提供了一些增强的方式来管理 回调,提供更加灵活的方式建立回调,而不用关心原始的回调是否已经触发。  同时值得注意的是,jQuery的递延对象支持多个回调绑定多个任务,任务本身可以既可以是同步也可以是异步的。

   您可以浏览下表中的递延功能,有助于了解哪些功能是你需要的:

 
jQuery.Deferred()     创建一个新的Deferred对象的构造函数,可以带一个可选的函数参数,它会在构造完成后被调用。
jQuery.when()     通过该方式来执行基于一个或多个表示异步任务的对象上的回调函数
jQuery.ajax()     执行异步Ajax请求,返回实现了promise接口的jqXHR对象
deferred.then(resolveCallback,rejectCallback)     添加处理程序被调用时,递延对象得到解决或者拒绝的回调。
deferred.done()     

当延迟成功时调用一个函数或者数组函数.
deferred.fail()     

当延迟失败时调用一个函数或者数组函数.。
deferred.resolve(ARG1,ARG2,...)     调用Deferred对象注册的‘done’回调函数并传递参数
deferred.resolveWith(context,args)     调用Deferred对象注册的‘done’回调函数并传递参数和设置回调上下文
deferred.isResolved     确定一个Deferred对象是否已经解决。
deferred.reject(arg1,arg2,...)     调用Deferred对象注册的‘fail’回调函数并传递参数
deferred.rejectWith(context,args)     调用Deferred对象注册的‘fail’回调函数并传递参数和设置回调上下文
deferred.promise()     返回promise对象,这是一个伪造的deferred对象:它基于deferred并且不能改变状态所以可以被安全的传递

 

  jQuery延时实现的核心是jQuery.Deferred:一个可以链式调用的构造函数。...... 需要注意的是任何deferred对象的默认状态是unresolved, 回调会通过 .then() 或 .fail()方法添加到队列,并在稍后的过程中被执行。  

  下面这个$.when() 接受多个参数的例子  

    function successFunc(){ console.log( “success!” ); }   
    function failureFunc(){ console.log( “failure!” ); }   
      
    $.when(   
       $.ajax( "/main.php" ),  
       $.ajax( "/modules.php" ),  
       $.ajax( “/lists.php” )  
     ).then( successFunc, failureFunc );   

  在$.when() 的实现中有趣的是,它并非仅能解析deferred对象,还可以传递不是deferred对象的参数,在处理的时候会把它们当做deferred对象并立 即执行回调(doneCallbacks)。 这也是jQuery的Deferred实现中值得一提的地方,此外,deferred.then()还为deferred.done和 deferred.fail()方法在deferred的队列中增加回调提供支持。

  利用前面介绍的表中提到的deferred功能,我们来看一个代码示例。 在这里,我们创建一个非常基本的应用程序:通过$.get方法(返回一个promise)获取一条外部新闻源(1)并且(2)获取最新的一条回复。  同时程序还通过函数(prepareInterface())实现新闻和回复内容显示容器的动画。

  为了确保在执行其他相关行为前,上面的这三个步骤确保完成,我们使用$.when()。根据您的需要 .then()和.fail() 处理函数可以被用来执行其他程序逻辑。

    function getLatestNews() {   
      return $.get( “latestNews.php”, function(data){   
         console.log( “news data received” );   
         $( “.news” ).html(data);   
       } );  
     }  
     function getLatestReactions() {  
        return $.get( “latestReactions.php”, function(data){   
           console.log( “reactions data received” );  
           $( “.reactions” ).html(data);  
        } );  
     }  
      
    function prepareInterface() {   
       return $.Deferred(function( dfd ) {   
           var latest = $( “.news, .reactions” );  
           latest.slideDown( 500, dfd.resolve );  
           latest.addClass( “active” );  
        }).promise();   
    }   
      
    $.when(   
        getLatestNews(), getLatestReactions(), prepareInterface()  
     ).then(function(){   
        console.log( “fire after requests succeed” );  
     }).fail(function(){   
        console.log( “something went wrong!” );  
     });   

  deferreds在ajax的幕后操作中使用并不意味着它们无法在别处使用。 在本节中,我们将看到在一些解决方案中,使用deferreds将有助于抽象掉异步的行为,并解耦我们的代码。


异步缓存

  当涉及到异步任务,缓存可以是一个有点苛刻的,因为你必须确保对于同一个key任务仅执行一次。因此,代码需要以某种方式跟踪入站任务。 例如下面的代码片段:

    $.cachedGetScript( url, callback1 );   
    $.cachedGetScript( url, callback2 );  

  缓存机制需要确保 脚本不管是否已经存在于缓存,只能被请求一次。 因此,为了缓存系统可以正确地处理请求,我们最终需要写出一些逻辑来跟踪绑定到给定url上的回调。

  值得庆幸的是,这恰好是deferred所实现的那种逻辑,因此我们可以这样来做:

    var cachedScriptPromises = {};   
    $.cachedGetScript = function( url, callback ) {  
         if ( !cachedScriptPromises[ url ] ) {   
            cachedScriptPromises[ url ] = $.Deferred(function( defer ) {  
                $.getScript( url ).then( defer.resolve, defer.reject );   
            }).promise();   
        }  
        return cachedScriptPromises[ url ].done( callback );  
    };  

  代码相当简单:我们为每一个url缓存一个promise对象。 如果给定的url没有promise,我们创建一个deferred,并发出请求。 如果它已经存在我们只需要为它绑定回调。 该解决方案的一大优势是,它会透明地处理新的和缓存过的请求。 另一个优点是一个基于deferred的缓存 会优雅地处理失败情况。 当promise以‘rejected’状态结束的话,我们可以提供一个错误回调来测试:

  $.cachedGetScript( url ).then( successCallback, errorCallback );

  请记住:无论请求是否缓存过,上面的代码段都会正常运作!


通用异步缓存

  为了使代码尽可能的通用,我们建立一个缓存工厂并抽象出实际需要执行的任务:

    $.createCache = function( requestFunction ) {   
        var cache = {};   
        return function( key, callback ) {   
            if ( !cache[ key ] ) {   
                cache[ key ] = $.Deferred(function( defer ) {  
                    requestFunction( defer, key );  
                 }).promise();   
            }   
            return cache[ key ].done( callback );  
        };   
    }   

  现在具体的请求逻辑已经抽象出来,我们可以重新写cachedGetScript:

    $.cachedGetScript = $.createCache(function( defer, url ) {  
          $.getScript( url ).then( defer.resolve, defer.reject );  
     });  

  每次调用createCache将创建一个新的缓存库,并返回一个新的高速缓存检索函数。现在,我们拥有了一个通用的缓存工厂,它很容易实现涉及从缓存中取值的逻辑场景。


图片加载

  另一个候选场景是图像加载:确保我们不加载同一个图像两次,我们可能需要加载图像。 使用createCache很容易实现:

    $.loadImage = $.createCache(function( defer, url ) {   
        var image = new Image();  
         function cleanUp() {  
            image.onload = image.onerror = null;   
        }  
         defer.then( cleanUp, cleanUp );  
         image.onload = function() {   
            defer.resolve( url );   
        };   
        image.onerror = defer.reject;   
        image.src = url;  
     });  

  接下来的代码片段如下:

    $.loadImage( "my-image.png" ).done( callback1 );   
    $.loadImage( "my-image.png" ).done( callback2 );   

  无论image.png是否已经被加载,或者正在加载过程中,缓存都会正常工作。


缓存数据的API响应

  哪些你的页面的生命周期过程中被认为是不可变的API请求,也是缓存完美的候选场景。 比如,执行以下操作:

    $.searchTwitter = $.createCache(function( defer, query ) {   
        $.ajax({   
            url: "http://search.twitter.com/search.json",   
            data: { q: query },   
            dataType: "jsonp",   
            success: defer.resolve,   
            error: defer.reject   
        });   
    });  

  程序允许你在Twitter上进行搜索,同时缓存它们:

    $.searchTwitter( "jQuery Deferred", callback1 );   
    $.searchTwitter( "jQuery Deferred", callback2 );  

定时

  基于deferred的缓存并不限定于网络请求;它也可以被用于定时目的。

  例如,您可能需要在网页上给定一段时间后执行一个动作,来吸引用户对某个不容易引起注意的特定功能的关注或处理一个延时问题。 虽然setTimeout适合大多数用例,但在计时器出发后甚至理论上过期后就无法提供解决办法。 我们可以使用以下的缓存系统来处理:

    var readyTime;   
    $(function() { readyTime = jQuery.now(); });   
    $.afterDOMReady = $.createCache(function( defer, delay ) {   
        delay = delay || 0;   
        $(function() {  
            var delta = $.now() - readyTime;   
            if ( delta >= delay ) { defer.resolve(); }   
            else {   
                setTimeout( defer.resolve, delay - delta );  
             }   
        });   
    });  

  新的afterDOMReady辅助方法用最少的计数器提供了domReady后的适当时机。 如果延迟已经过期,回调会被马上执行。


同步多个动画

  动画是另一个常见的异步任务范例。 然而在几个不相关的动画完成后执行代码仍然有点挑战性。尽管在jQuery1.6中才提供了在动画元素上取得promise对象的功能,但它是很容易的手动实现:

    $.fn.animatePromise = function( prop, speed, easing, callback ) {   
        var elements = this;   
        return $.Deferred(function( defer ) {   
            elements.animate( prop, speed, easing, function() {   
            defer.resolve();   
            if ( callback ) {   
            callback.apply( this, arguments );  
             }   
            });   
        }).promise();  
    };  

   然后,我们可以使用$.when()同步化不同的动画:

    var fadeDiv1Out = $( "#div1" ).animatePromise({ opacity: 0 }),   
          fadeDiv2In = $( "#div1" ).animatePromise({ opacity: 1 }, "fast" );   
      
    $.when( fadeDiv1Out, fadeDiv2In ).done(function() {   
         /* both animations ended */  
     });  

  我们也可以使用同样的技巧,建立了一些辅助方法:

    $.each([ "slideDown", "slideUp", "slideToggle", "fadeIn", "fadeOut", "fadeToggle" ],   
    function( _, name ) {   
        $.fn[ name + "Promise" ] = function( speed, easing, callback ) {  
            var elements = this;   
            return $.Deferred(function( defer ) {   
                elements[ name ]( speed, easing, function() {   
                    defer.resolve();   
                    if ( callback ) {   
                    callback.apply( this, arguments );   
                    }   
                });  
             }).promise();   
        };   
    });  

  然后想下面这样使用新的助手代码来同步动画:

    $.when(   
        $( "#div1" ).fadeOutPromise(),   
        $( "#div2" ).fadeInPromise( "fast" )   
    ).done(function() {  
        /* both animations are done */  
     });  
       

一次性事件

  虽然jQuery提供你可能需要的所有的时间绑定方法,但当事件仅需要处理一次时,情况可能会变得有点棘手。( 与$.one() 不同 )

  例如,您可能希望有一个按钮,当它第一次被点击时打开一个面板,面板打开之后,执行特定的初始化逻辑。 在处理这种情况时,人们通常会这样写代码:

    var buttonClicked = false;   
    $( "#myButton" ).click(function() {   
        if ( !buttonClicked ) {   
            buttonClicked = true;   
            initializeData();   
            showPanel();   
        }   
    });  

  不久后,你可能会在面板打开之后点击按钮时添加一些操作,如下:

    if ( buttonClicked ) { /* perform specific action */ }  

  这是一个非常耦合的解决办法。 如果你想添加一些其他的操作,你必须编辑绑定代码或拷贝一份。 如果你不这样做,你唯一的选择是测试buttonClicked。由于buttonClicked可能是false,新的代码可能永远不会被执行,因此你 可能会失去这个新的动作。

  使用deferreds我们可以做的更好 (为简化起见,下面的代码将只适用于一个单一的元素和一个单一的事件类型,但它可以很容易地扩展为多个事件类型的集合):

    $.fn.bindOnce = function( event, callback ) {   
        var element = $( this[ 0 ] ),   
        defer = element.data( "bind_once_defer_" + event );   
        if ( !defer ) {   
            defer = $.Deferred();   
            function deferCallback() {   
                element.unbind( event, deferCallback );   
                defer.resolveWith( this, arguments );   
            }   
            element.bind( event, deferCallback )   
            element.data( "bind_once_defer_" + event , defer );   
        }   
        return defer.done( callback ).promise();   
    };  

  该代码的工作原理如下:

    检查该元素是否已经绑定了一个给定事件的deferred对象
    如果没有,创建它,使它在触发该事件的第一时间解决
    然后在deferred上绑定给定的回调并返回promise

  代码虽然很冗长,但它会简化相关问题的处理。 让我们先定义一个辅助方法:

    $.fn.firstClick = function( callback ) {   
           return this.bindOnce( "click", callback );  
     };  

  然后,之前的逻辑可以重构如下:

    var openPanel = $( "#myButton" ).firstClick();   
    openPanel.done( initializeData );   
    openPanel.done( showPanel );  

  如果我们需要执行一些动作,只有当面板打开以后,所有我们需要的是这样的:

    openPanel.done(function() { /* perform specific action */ });     

  如果面板没有打开,行动将得到延迟到单击该按钮时。


组合助手

  单独看以上每个例子,promise的作用是有限的 。 然而,promise真正的力量是把它们混合在一起。

 

在第一次点击时加载面板内容并打开面板

  假如,我们有一个按钮,可以打开一个面板,请求其内容然后淡入内容。使用我们前面定义的助手方法,我们可以这样做:

    var panel = $( "#myPanel" );   
    panel.firstClick(function() {    
        $.when(   
            $.get( "panel.html" ),   
            panel.slideDownPromise()  
        ).done(function( ajaxResponse ) {   
            panel.html( ajaxResponse[ 0 ] ).fadeIn();   
        });  
    });  

在第一次点击时载入图像并打开面板

  假如,我们已经的面板有内容,但我们只希望当第一次单击按钮时加载图像并且当所有图像加载成功后淡入图像。HTML代码如下:

       
     
      
        
       
     

  我们使用data-src属性描述图片的真实路径。 那么使用promise助手来解决该用例的代码如下:

    $( "#myButton" ).firstClick(function() {   
        var panel = $( "#myPanel" ),   
        promises = [];   
        $( "img", panel ).each(function() {   
            var image = $( this ), src = element.attr( "data-src" );   
            if ( src ) {   
            promises.push(   
                $.loadImage( src ).then( function() {  
                        image.attr( "src", src );   
                    }, function() {   
                        image.attr( "src", "error.png" );  
                } )   
            );  
            }   
        });   
      
        promises.push( panel.slideDownPromise() );   
      
        $.when.apply( null, promises ).done(function() { panel.fadeIn(); });   
    });  

  这里的窍门是跟踪所有的LoadImage 的promise,接下来加入面板slideDown动画。 因此首次点击按钮时,面板将slideDown并且图像将开始加载。 一旦完成向下滑动面板和已加载的所有图像,面板才会淡入。


在特定延时后加载页面上的图像

  假如,我们要在整个页面实现递延图像显示。 要做到这一点,我们需要的HTML的格式如下:

       
       
       
     

  意思非常简单:

    image1.png,第三个图像立即显示,一秒后第一个图像显示
    image2.png 一秒钟后显示第二个图像,两秒钟后显示第四个图像

  我们将如何实现呢?

    $( "img" ).each(function() {  
        var element = $( this ),  
            src = element.attr( "data-src" ),  
            after = element.attr( "data-after" );  
        if ( src ) {  
            $.when(  
                $.loadImage( src ),  
                $.afterDOMReady( after )  
            ).then(function() {  
                element.attr( "src", src );  
            }, function() {  
                element.attr( "src", "error.png" );  
            } ).done(function() {  
                element.fadeIn();  
            });  
        }  
    });  

   如果我们想延迟加载的图像本身,代码会有所不同:

    $( "img" ).each(function() {  
        var element = $( this ),  
            src = element.attr( "data-src" ),  
            after = element.attr( "data-after" );  
        if ( src ) {  
            $.afterDOMReady( after, function() {  
                $.loadImage( src ).then(function() {  
                    element.attr( "src", src );  
                }, function() {  
                    element.attr( "src", "error.png" );  
                } ).done(function() {  
                    element.fadeIn();  
                });  
            } );  
        }  
    });  

  这里,我们首先在尝试加载图片之前等待延迟条件满足。当你想在页面加载时限制网络请求的数量会非常有意义。


结论

  正如你看到的,即使在没有Ajax请求的情况下,promise也非常有用的。通过使用jQuery 1.5中的deferred实现 ,会非常容易的从你的代码中分离出异步任务。 这样的话,你可以很容易的从你的应用程序中分离逻辑。

本文来源:http://www.bbyears.com/jiaocheng/89152.html

热门标签

更多>>

本类排行