1. Avant-propos
Avant jQuery 1.5, si plusieurs opérations Ajax étaient nécessaires, nous utilisions généralement les deux méthodes suivantes :
1).Appel en série Ajax
$.ajax({ success: function() { $.ajax({ success: function() { $.ajax({ //callbacks... }); }); });
Le code de cette méthode est peu lisible, inefficace, obscur et compliqué à déboguer et à dépanner.
2). Appeler Ajax en parallèle
var promises = []; $.ajax({ success: function() { promises.push('resolved'); check(); } }); $.ajax({ success: function() { promises.push('resolved'); check(); } }); $.ajax({ success: function() { promises.push('resolved'); check(); } }); var check = function() { //checks for all 3 values in the promises array }
Cette méthode est déjà très bonne pour les appels de fonctions de rappel. Elle obtient des données en parallèle et a une bonne lisibilité. Les inconvénients sont un code long, une faible évolutivité et une grande complexité de débogage et de dépannage.
Après jQuery 1.5, des objets différés ont été ajoutés. Par conséquent, les mêmes exigences que ci-dessus peuvent être satisfaites de la manière suivante.
1) Promesse
var address = $.ajax({}); var tweets = $.ajax({}); var facebook = $.ajax({}); render_side_bar = function(address, tweets, facebook){ //render sidebar } render_no_side_bar = function () { } $.when(address, tweets, facebook).then(render_side_bar, render_no_side_bar)
On peut voir que le code a une bonne lisibilité, une grande évolutivité et réduit considérablement la complexité du débogage et du dépannage.
Alors la question est : que sont exactement les promesses et les objets différés ?
2. Explication détaillée
2. Qu'est-ce qu'un objet différé ?
L'objet différé est un objet différé. Il s'agit d'une solution de fonction de rappel introduite dans jQuery version 1.5. Elle représente une certaine opération à effectuer et fournit quelques méthodes pour aider les utilisateurs à l'utiliser.
L'objet différé est l'implémentation de l'interface Promises. L'objet jqXHR renvoyé par toutes les versions Ajax de jQuery 1.5 et versions ultérieures est un objet différé.
Plusieurs avantages des objets différés
2.1. Spécifiez plusieurs fonctions de rappel pour la même opération
L'un des avantages des objets différés est qu'ils vous permettent d'ajouter plusieurs fonctions de rappel à une opération, ce qui n'est pas possible dans l'Ajax traditionnel.
$.ajax("test.html") .done(function(){ alert("first success callback!");} ) .fail(function(){ alert("there is an error!"); } ) .done(function(){ alert("second success callback!");} );
2.2. Spécifier la même fonction de rappel pour plusieurs opérations
Le deuxième avantage de l'objet différé est qu'il vous permet de spécifier la même fonction de rappel pour plusieurs opérations, ce qui est également impossible à réaliser en ajax traditionnel.
$.when($.ajax({}), $.ajax({})) .done(function(){ alert("success!"); }) .fail(function(){ alert("error!"); });
2.3. Fonction de rappel pour les opérations non-Ajax
Le troisième avantage de l'objet différé est qu'il n'est plus limité aux opérations ajax. Toute opération (opération ajax ou opération locale/opération asynchrone ou opération synchrone) peut utiliser l'objet différé et spécifier une fonction de rappel.
Une opération chronophage très typique
var dfd = $.Deferred(); // create a deferred object var wait = function(dtd){ var tasks = function(){ alert("over!"); dtd.resolve(); // change the state of the deferred object from pending to resolved }; setTimeout(tasks,50000); return dtd; };
$.when(wait(dtd)) .done(function(){ alert("success!"); }) .fail(function(){ alert("error!"); });
2.4. Appel en chaîne
L'opération ajax traditionnelle dans jQuery ressemble à ceci :
$.ajax({ url: "", success: function(){ alert("success!"); }, error:function(){ alert("error!"); } });
Success spécifie la fonction de rappel après la réussite de l'opération ajax, et error spécifie la fonction de rappel après l'échec de l'opération ajax. Avant jQuery 1.5, les opérations Ajax renvoyaient un objet XMLHTTPRequest et ne prenaient pas en charge les opérations en chaîne. À partir de la version 1.5, l'opération ajax renvoie l'objet jqXHR, qui est un objet différé. Un avantage important de l'objet différé est qu'il peut effectuer des opérations en chaîne, car toutes les méthodes de l'objet différé renvoient des objets différés.
L'opération ajax actuelle s'écrit :
$.ajax({}) .done(function(){ alert("success!"); }) .fail(function(){ alert("fail!"); });
En comparant les deux méthodes d'écriture, on peut clairement voir que done() est équivalente à la méthode de réussite d'une opération ajax traditionnelle, et fail() est équivalente à la méthode d'échec d'une opération ajax traditionnelle. Par rapport aux méthodes d'écriture traditionnelles, la lisibilité du code est améliorée.
3.Méthodes d'objets différés
3.1 Utilisation de base
(1).Générer un objet différé
var dfd = $.Deferred(); //créer un objet différé
(2).État de l'objet différé
Les objets différés ont trois états
en attente : indique que l'opération est dans un état inachevé et que tout objet différé (différé) démarre dans l'état en attente.
résolu : indique que l'opération a réussi.
rejeté : indique que l'opération a échoué.
La méthode state() renvoie l'état actuel de l'objet différé.
$.Deferred().state(); // 'pending' $.Deferred().resolve().state(); // 'resolved' $.Deferred().reject().state(); // 'rejected'
(3).Changer l'état de l'objet différé
Appelez deferred.resolve() ou deferred.resolveWith() pour convertir Deferred en état résolu et exécuter immédiatement tous les doneCallbacks dans le paramètre.
var callbackFunc = function(){console.log(arguments[0]);} var dfd = $.Deferred(); dfd.done(callbackFunc); dfd.resolve("hello"); //'hello'
Appelez deferred.reject() ou deferred.rejectWith() pour convertir Deferred en statut rejeté et exécuter immédiatement tous les failCallbacks dans le paramètre.
var callbackFunc = function(){console.log(arguments[0]);} var dfd = $.Deferred(); dfd.fail(callbackFunc); dfd.reject("fail"); //'fail'
(4).Fonction de rappel de liaison
Lorsque l'état de l'objet différé change, la fonction de rappel sera déclenchée. Tous les rappels ajoutés à cet objet à l'aide de deferred.then(), deferred.always(), deferred.done() ou deferred.fail() sont mis en file d'attente pour exécution.
en attente-->résolu, exécutez tous les doneCallbacks (spécifiés par done()) dans les paramètres, et les paramètres sont transmis à doneCallbacks par résolu.
en attente -> rejeté, exécutez tous les failCallbacks (spécifiés par fail()) dans les paramètres, et les paramètres sont transmis à failCallbacks par résolu.
en attente -> résolu/rejeté, exécute les rappels spécifiés par toujours () et les paramètres sont transmis aux rappels résolus.
var f1 = function(){console.log("done");}, f2 = function(){console.log("fail");}, f3 = function(){console.log("always");}; var dfd = $.Deferred(); dfd.done(f1).fail(f2).always(f3); //if dfd.resolve(); //'done' 'always' //if dfd.reject(); //'fail' 'always'
如果在状态更改后附件一个callback则会立即执行callback,因此不必担心deferred对象何时被resolved或者rejected,因为无论何时,参数都会正确地传递给callbacks。
var fun1 = function(){console.log(arguments[0]);}, fun1 = function(){console.log(arguments[0]);}; var dfd = $.Deferred(); dfd.done(fun1); dfd.resolve("hello"); //'hello' dfd.done(fun2); //'hello'
3.2.deferred对象的方法
(1)$.Deferred([beforeStart]) -- 创建一个deferred对象,参数类型为Function,是一个在构造函数之前调用的函数。
var func = function(){console.log("start");} var dfd = $.Deferred(func); //'start' create a deferred object
(2)deferred.done(doneCallbacks [,doneCallbacks]) -- 当deferred(延迟)对象解决时,调用添加处理程序。
args:接受一个或者多个参数,所有的参数都可以是一个单一的函数或者函数数组,当deferred(延迟)对象解决时,doneCallbacks被调用。回调是依照他们添加的顺序执行的。
var func1 = function(){console.log("1");}, func2 = function(){console.log("2");}, func3 = function(){console.log("3");}; var dfd = $.Deferred(); dfd.done([func1,func2],func3,[func2,func1]); dfd.resolve(); // "1 2 3 2 1"
(3)deferred.fail(failCallbacks [,failCallbacks]) -- 当deferred(延迟)对象拒绝时,调用添加处理程序。
args:接受一个或者多个参数,所有的参数都可以是一个单一的函数或者函数数组,当deferred(延迟)对象拒绝时,failCallbacks被调用。回调是依照他们添加的顺序执行的。
var func1 = function(){console.log("1");}, func2 = function(){console.log("2");}, func3 = function(){console.log("3");}; var dfd = $.Deferred(); dfd.fail([func1,func2],func3,[func2,func1]); dfd.reject(); // "1 2 3 2 1"
(4)deferred.resolve(args) and deferred.resolveWith(context [,args]) -- 解决Deferred(延迟)对象,并根据给定的args参数(resolveWith给定context)调用任何doneCallbacks。
参数:args -- type(object),传递给回调函数(doneCallbacks)的可选的参数,
context -- type(object),Context(上下文)作为this对象传递给完成回调函数(doneCallbacks)。
var func = function(arg){console.log(arg);}; $.Deferred().done(func).resolve("done!"); //'done!' var func = function(arg1,arg2){console.log(arg1.name + ',' + arg2);}; $.Deferred().done(func).resolve({name:'Lucy'},'How are you!'); // 'Lucy,How are you!'
resolve和resolveWith的区别就等同于fire和fireWith的区别。
var func = function () { console.log(this.name + ',' + arguments[0] + ' ' + arguments[1] + ' ' + arguments[2]); }; $.Deferred().done(func).resolveWith({ name: "Lucy" }, ["How", "are", "you!"]);//'Lucy,How are you!'
(5)deferred.reject(args) and deferred.rejectWith(context [,args]) -- 拒绝Deferred(延迟)对象,并根据给定的args参数(rejectWith给定context)调用任何failCallbacks。
参数:args -- type(object),传递给回调函数(doneCallbacks)的可选的参数,
context -- type(object),Context(上下文)作为this对象传递给完成回调函数(doneCallbacks)。
var func = function(arg){console.log(arg);}; $.Deferred().fail(func).reject("error!"); //'error!' var func = function(ctx,arg){console.log(ctx.name + ',' + arg);}; $.Deferred().fail(func).reject({name:'Mark'},'What happend!'); // 'Mark,What happend!'
reject和rejectWith的区别就等同于fire和fireWith的区别。
var func = function () { console.log(this.name + ',' + arguments[0] + ' ' + arguments[1]); }; $.Deferred().fail(func).rejectWith({ name: "Mark" }, ["what", "happend!"]); // 'Mark,What happend!'
(6)deferred.promise([target]) -- 返回Deferred(延迟)的Promise(承诺)对象。
参数可选,无参数时返回一个Promise(承诺)对象,Promise(承诺)对象仅会暴露那些需要绑定额外的处理或判断状态的延迟方法(then, done, fail, always,pipe, progress, state,和 promise)时,并不会暴露任何用于改变状态的延迟方法(resolve, reject, notify,resolveWith, rejectWith, 和 notifyWith)。使用Promise(承诺)会阻止其他人破坏你制造的promise。
function asyncEvent() { var dfd = jQuery.Deferred(); // Resolve after a random interval setTimeout(function () { dfd.resolve("hurray"); }, Math.floor(400 + Math.random() * 2000)); // Reject after a random interval setTimeout(function () { dfd.reject("sorry"); }, Math.floor(400 + Math.random() * 2000)); // Show a "working..." message every half-second setTimeout(function working() { if (dfd.state() === "pending") { dfd.notify("working... "); setTimeout(working, 500); } }, 1); // Return the Promise so caller can't change the Deferred return dfd.promise(); } // Attach a done, fail, and progress handler for the asyncEvent $.when(asyncEvent()).then( function (status) { alert(status + ", things are going well"); }, function (status) { alert(status + ", you fail this time"); }, function (status) { alert(status); } );
有参数时,会将事件绑定到参数上,然后返回该参数对象(返回的实际是一个扩展的Promise(承诺)对象)。
var obj = { hello: function (name) { alert("Hello " + name); } }, // Create a Deferred dfd = $.Deferred(); // Set object as a promise dfd.promise(obj); // Resolve the deferred dfd.resolve("John"); // Use the object as a Promise obj.done(function (name) { obj.hello(name); // will alert "Hello John" }).hello("Karl");
(7)$.when(deferreds) -- 提供一种方法来执行一个或多个对象的回调函数。
参数:type(Deferred),一个或多个延迟对象,或者普通的JavaScript对象。
参数仅传入一个单独的Deferred对象,返回它的Promise对象。
function func() { var dfd = $.Deferred(); setTimeout(function () { dfd.resolve("hurry"); }, 500); return dfd.promise(); }; $.when(func()).done(function (arg) { alert(arg); /*alert "hurry"*/ });
参数传入一个非Deferred和Promise对象,那么该参数会被当成一个被解决(resolved)的延迟对象,并且绑定到上面的任何doneCallbacks都会被立即执行。
$.when( { name: 123 } ).done( function(arg) { alert(arg.name); } /* alerts "123" */ );
无参数,返回一个resolved(解决)状态的Promise对象。
$.when().state(); // "resolved"
参数为多个Deferred对象,该方法根据一个新的“宿主” Deferred(延迟)对象,跟踪所有已通过Deferreds聚集状态,返回一个Promise对象。当所有的延迟对象被解决(resolve)时,“宿主” Deferred(延迟)对象才会解决(resolved)该方法,或者当其中有一个延迟对象被拒绝(rejected)时,“宿主” Deferred(延迟)对象就会reject(拒绝)该方法。
var d1 = $.Deferred(); var d2 = $.Deferred(); $.when( d1, d2 ).done(function ( v1, v2 ) { console.log( v1 ); // "Fish" console.log( v2 ); // "Pizza" }); d1.resolve( "Fish" ); d2.resolve( "Pizza" );
(8)deferred.then(doneFilter [,failFilter] [,progressFilter]) -- 当Deferred(延迟)对象解决,拒绝或仍在进行中时,调用添加处理程序。
参数:
doneFilter -- type(Function),当Deferred(延迟)对象得到解决时被调用的一个函数。
failFilter -- type(Function),当Deferred(延迟)对象拒绝时被调用的一个函数,可选。
progressFilter -- type(Function),当Deferred(延迟)对象生成进度通知时被调用的一个函数,可选。
其实,then方法可以理解成,把done(),fail(),progress()合在一起写。
var filterResolve = function () { var dfd = $.Deferred(), filtered = dfd.then(function (value) { return value * 2; }); dfd.resolve(5); filtered.done(function (value) { console.log(value); }); }; filterResolve(); //'10' var defer = $.Deferred(), filtered = defer.then(null, function (value) { return value * 3; }); defer.reject(6); filtered.fail(function (value) { alert("Value is 3*6 = " + value); });
(9)deferred.always(alwaysCallbacks [,alwaysCallbacks]) -- 当Deferred(延迟)对象解决或拒绝时,执行alwaysCallbacks。
顾名思义,只要Deferred对象的状态发生更改(解决或者拒绝)均会调用alwaysCallbacks。
(10)deferred.state() -- 获取一个Deferred(延迟)对象的当前状态,不接受任何参数。
$.Deferred().state();//"pending"
上面讲述过deferre(延迟)对象的三种状态,这个方法对于debug非常有用,例如,在准备reject一个deferred对象之前,判断它是否处于resolved状态。
(11)deferred.notify(args) and deferred.notifyWith()
(12)deferred.progress()
(13)deferred.pipe()
(14).promise()
(15)deferred.isRejected() 和 deferred.isResolved() -- 从jQuery 1.7开始被弃用,较新版本的jQuery类库中已经被删除,可以使用state()方法代替这两个方法。
(16)deferred.pipe() -- 从jQuery 1.8开始被弃用。
4.什么情况下使用deferred对象和Promises?
上面讲了很多,那么我们究竟在什么情况下使用Deferred对象和Promises对象呢?
(1)复杂的动画
不知道动画什么时候结束,但是又必须在动画结束的时候做一些操作或者是启动其他的动画,这种情况下,如果采用其他的方式,很容易导致代码可读性差,尤其是还夹带着一些其它的操作,比如渲染、表单操作等,现在jQuery会为你的动画操作返回一个Promise,这样这些动画可以进行链式操作。
(2)处理队列
function wait(ms) { var deferred = $.Deferred(); setTimeout(function(){deferred.resolve()}, ms); return deferred.promise(); } wait(1500).then(function () { // After 1500ms this will be executed });
(4)典型的Ajax操作
$.when($.ajax({}), $.ajax({})) .done(function(){ alert("success!"); }) .fail(function(){ alert("error!"); });
(5)一些耗时的大循环操作
以上就是本文的全部内容,希望对大家的学习有所帮助。