[译文]jQuery的Deferred

[译文]jQuery的Deferred


   jQuery的$.Deferred()命令是该函数库中近来最强大的命令。它并非是一个新观念,但对于数以千计的前端开发人员来说是极有价值的。从核心来看,Deferred非常的简单,但却是一个很强大的异步管理的工具。众所周知开发前端时,经常需要进行异步的处理。

   我们把焦点放在Deferred以及jQuery在这方面所提供的API。下面有许多丰富的范例。读完后,你将会了解到什么是Deferred以及何时使用它。

了解核心概念

   Deferred本质上就是一个Proxy对象,它可以套用到任何异步处理: Ajax请求,动画,或是Web Worker。使用者行为在设计上可以采用延迟处理;在页面上的表单,Deferred允许你可以指定当发生错误或顺利完成时要进行怎样的处理。jQuery允许你注册链函数,该函数会在Deferred判定成功时,或是发生错误,或是告知正处于某种状态时被调用。

   你可能已经使用过Deferred。jQuery的Ajax方法回传的对象已经实做了Deferred界面。这个对象会解析Ajax请求是成功或是失败。

有一个很重要你必须要知道的东西

   Deferred抽象来看是让开发人员避开异步处理。Deferred可以无限延展,而链函数可以持续的在Deferred对象生命周期中添加。这种手法的关键在于Deferred的链函数会在Deferred解析完状态之后立刻被调用。你不需要去担心异步的计算单元(例:Ajax请求)是否已完成。系结到Deferred的链函数会在Deferred在当下或待会解析出执行状态后被执行。

使用jQuery的Deferred

解析和拒绝

   Deferred的核心方法就是你需要去处理Deferred对象的解析或拒绝。你可以使用$.Deferred()方法创建一个新的Deferred对象。该对象的done()方法会在Deferred被解析被执行,而fail()方法则是在Deferred对象被拒绝时执行。实际上,解析或拒绝一个Deferred对象将以借由resolve()和reject()来达成。本质上,jQuery的ajax()方法会在请求成功时调用resolve()方法,而会在请求是错误(例: Http状态为404)时调用reject()方法。

   要记得,若你挂载done或fail处理到deferred对象上,且该对象已经解析过了,该函数会立马被执行。

通知Deferreds: notify()和progress()

   jQuery 1.7亦添加了Progress的概念到Deferred中,这个Progress是针对拒绝和解析用的。progress()允许你挂载Deferred调用notify()方法时的链函数。这让Deferred得以响应"解析状态的目前进度"。Deferred描述一个长时间资源叫用时,就可以在progress()函数上注册一个链函数,并且该函数可以周期性的更新进度列,举例来说,Deferred对象会在载入/载入完成后解析这些状态时被通知目前进度。

回传promis()

   在许多案例中,假若你正回传一个Deferred,但你并不想要让这个对象被解析或拒绝-你想要自主控制。在这个情况下,你可以回传promise对象。在jQuery的术语中,promise是一种只读型的Deferred对象。promise允许你挂载链函数并且询问Deferred的状态,但你无法要它改变它自己的状态(例: 解析/拒绝)。jQuery的ajax()方法回传的是promise,因为它自己内部有处理Ajax请求的判定是正确或错误。

借由when()同步所有异步事件

   $.when()允许一或多个Deferred并且产生一个新的Deferred对象, 该对象只有在所有Deferred对象都解析后才会解析。这可以让你去组合多个异步事件成为一个。

   思考下面的范例:

      我们的UI请求数据来自于两个独立的Ajax请求,并且它需要这两个请求都取得数据后才能绘出画面。

   没有when(),我会将焦点放在巢状式链函数来确保两个请求已经完成。代价为何?我们需要在两个不同的地方指定错误处理流程。


var name = $.post('/echo/json/', {json:JSON.stringify({'name': "Matt Baker"})});
var lastUpdate = $.post('/echo/json/', {json:JSON.stringify({'lastUpdate':"Hello World"})});
name.done(function(nameData) { 
   var name = nameData.name;
   lastUpdate.done(function(lastUpdateData) {
      var lastUpdate = lastUpdateData.lastUpdate;
      $("#render-me").html(name + " 's last update was: " + lastUpdate);
   }).fail(function() {
      $("#error").html("an error occured").show();
   }).fail(function() {
       $("#error").html("an error occured").show();
   });
});

   我们可以使用$.when()来组合两个Ajax请求所回传的Deferred成为一个Deferred对象。该对象只会在两个Ajax请求都完成时才会被解析。我们可以注册一个链函数用来处理UI元素在成功时的呈现。附带一题,我们仅需指定一次错误处理在一个地方。


var name = $.post('/echo/json/', {json:JSON.stringify({'name':"Matt Baker"}) });
var lastUpdate = $.post('/echo/json', {json:JSON.stringify({'lastUpdate':"Hello World"})});

$.when(name, lastUpdate)
  .done(function(nameResponse, lastUpdateResponse) {
      var name = nameResponse[0].name;
      var lastUpdate = lastUpdateResponse[0].lastUpdate;
          $("#render-me").html(name+"'s last update was: "+lastUpdate);
  }).fail(function() { 
       $("#error").html("an error occured").show();
});

//为什么数组会出现在done函数中?

//新的done链函数接收所有来自每一个在$.when()中的Deferred对象的done链函数所接收到的数据。

//在本例中,我们取得两个数组,一个是来自传送到done方法链函数的name请求,另一个是传送到done方法链函数的lastUpdate请求。

转换型式成pipe()

   jQuery中有一个令人感兴趣的Deferred对象的API: pipe()。pipe()允许你将Deferred的结果转型(jQuery称其为过滤器)。你可以提供一个函数用来修改reject()或resolove()传入你链函数的值,或是该函数可以修改Deferred对象的状态。

   让我们来看一个范例。考虑以下情境。

      你的JSON API是借由JSON响应的某个旗标来描述错误(例: {error:true})而不是借由传送Http状态码来标示错误。你的JSON API总是回传Http状态码200。

   在此状况下,假入在Http状态为200下的错误发生了。由于promise只有在Http请求失败时才会是拒绝。这样一来你就必须要自行处理你的错误处理流程于success的链函数中,而不是fail的链函数中。


$.post('/echo/json/',{json: JSON.stringify({'error':true}) })
   .done(function(response) {
      if(response.error) {
          $("#status").html("An error occurred");
      }else {
         $("#status").html("Success!");
      }
}).fail(function(response) {
    $("#status").html("A server error occured(failing http status code)");
});

   很明显地,这不是一个理想的解决方案。借由强大的pipe(),我们就可以创建一个新的Deferred对象,它可以栏截Ajax请求的解析。我们会检查JSON响应中的error旗标。假若为true,则pipe将回传一个已拒绝的Deferred对象,它会令fail的链函数被执行。


var myXhrDeferred = 
   $.post('/echo/json', {json:JSON.stringify({'error':true})})
      .pipe(function(response) {
         if(response.error){
            return $.Deferred().reject(response);
         }
         return response;
      },
      function() {
         return $.Deferred().reject({error:true});
      }
   );

myXhrDeferred .done(function(response) {
   $("#status").html("Success!");
}).fail(function(response) {
   $("#status").html("An error occurred");
});

全民异步

   实际上,在你的网站或是应用程序上有着许多的异步。考虑如下情境:

      你的网站要求使用者创建一个会员数据。为了要鼓励他们把数据填完,你会显示一个数据完整度进度表。当它们填完后你会想要显示一个"感谢"的消息。

   在此案例中,Deferred描述的是会员数据的延迟程度。本质上是在创建或计算填写数据实际上却是计算人类的行为。在这个状况下你可能不会去考虑到异步,但可以借由Deferred来进行验证。我们可以使用notify()来通知使用者行为的Deferred对象(也因此Deferred成了Proxy),并且resolve()可以通知已完成。我们可以在progress()上注册链函数来更新进度表,并且使用done()来显示"感谢"消息。


var userProgress = $.Deferred();
var $profileFileds = $("input");
var totalFields = $profileFields.length;
userProgress.progress(function(filledFields) {
var pctComplete = (filledFields/totalFields)*100;
     $("#progress").html(pctComplete.toFixed(0));
});

userProgress.done(function() {
     $("#thanks").html("Thanks for completing your profile!").show();
});

$("input").on("change", function() {
   var filledFields = $profileFields.filter("[value!='']").length;
   userProgress.notify(filledFields);
   if(filledFields == totalFields) {
      userProgress.resolve();
   }
});

Deferred无所不在

   为了处理每个开发人员都得面对的异步处理,Deferred模式是简单,强大,并且广泛应用的:

   1. 在缓存中使用Deferred

   2. 更有描述力的setTimeout()

   3. jQuery Deferred API的外加范例

   4. jQuery Deferred对象API参考

原文链接:http://eng.wealthfront.com/2012/12/jquerydeferred-is-most-important-client.html

原文:大专栏  [译文]jQuery的Deferred


猜你喜欢

转载自www.cnblogs.com/chinatrump/p/11496661.html
今日推荐