David Soyez / Développeur Web
Sommaire
David Soyez / Développeur Web
David Soyez / Développeur Web
Compatibilité
David Soyez / Développeur Web
...ok, mais concrètement ça donne quoi?
Avantages
David Soyez / Développeur Web
Exemple sans l'utilisation de deferred:
function getData(callback) {
$.ajax( "/echo/json/", {
data: {
json: JSON.stringify({value:new Date().getSeconds()})
},
method: 'POST',
success: function(data) {
var result = parseInt(data.value) + parseInt($('#value').val());
callback(result);
}
} );
}
jQuery('button').on('click', function() {
// get the remote template
$.ajax( "/echo/html/", {
data: {
html: '<div id="template" style="padding-left:10px;color:red;"></div>'
},
method: 'POST',
success: function(html) {
// affiche le template
$('body').append(html);
// récupère les données sur le serveur distant
getData(function(data) {
$('#template').html(data);
});
}
});
});
David Soyez / Développeur Web
Exemple avec l'utilisation de deferred:
function getTemplate() {
return $.ajax( "/echo/html/", {
data: {
html: '<div id="template" style="padding-left:10px;color:red;"></div>'
},
method: 'POST'
});
}
function getData() {
var dfd = $.Deferred();
$.ajax( "/echo/json/", {
data: {
json: JSON.stringify({value:new Date().getSeconds()})
},
method: 'POST'
}
)
.done(function(data) {
// traitement des données
var result = parseInt(data.value) + parseInt($('#value').val());
dfd.resolve(result);
} );
return dfd.promise();
}
jQuery('button').on('click', function() {
// get the remote template
$.when(getTemplate())
.then(function(html) {
$('body').append(html);
})
.then(getData)
.then(function(data) {
$('#template').html(data);
});
});
David Soyez / Développeur Web
Autre exemple:
var User = {
getUser: function( id ) {
var dfd = $.Deferred();
// récupère l'utilisateur via l'api
// ...
// resolve le deferred en transmettant les données
dfd.resolve({
id: id,
name: 'David'
});
return dfd.promise();
}
};
User.getUser(1)
.done(function(user) {
alert(user.name);
});
David Soyez / Développeur Web
Quand les utiliser?
David Soyez / Développeur Web
L'objet Deferred
L'objet Deferred permet d'ajouter des callbacks à une pile et de relayer le succès ou l'échec de fonctions synchrones ou asynchrones.
Un exemple simple sans aucune utilité resemblerait à ceci:
var dfd = $.Deferred();
dfd.done(function(value) {
alert(value);
});
dfd.resolve("Salut!");
L'objet Deferred comprend deux méthodes importantes:
et comprend 5 méthodes par lequel attacher des callbacks:
David Soyez / Développeur Web
L'objet Promise
L'objet Promise est une méthode importante de Deferred qui permet à l'utilisateur d'y écouter les événements "done", "fail" etc.. d'un deferred.
Il est séparé du deferred pour empêcher l'utilisateur de le manipuler et de le "resolve", c'est pourquoi il contient les mêmes méthodes que Deferred sans les méthodes resolve() et reject()
Pour résumé, une promise peut seulement être "resolve" par son deferred associé.
function maFonctionAsync() {
var dfd = $.Deferred(); // on créé un deferred pour cette méthode
setTimeout(dfd.resolve, 1000); // on resolve le deferred
return dfd.promise(); // on créé et retourne une promise
}
$.when(maFonctionAsync()) // reçoit la promise (l'utilisateur n'a pas accès au resolve)
.then(function() { // est éxécuté une fois que le deferred de la promise est resolved
alert('promise resolved');
})
Voici un exemple simple d'une promise et son deferred:
David Soyez / Développeur Web
Deferred vs. Promise
L'objet Promise fonctionne conjointement avec l'objet Deferred et elles contiennent presque toutes les deux les mêmes méthodes. La seule différence est que Promise ne peut pas "resolve" ou "reject" le deferred. C'est le deferred qui doit s'en charger.
// en retournant une promise l'utilisateur ne pourra pas "resolve","reject" le deferred :
function privateResolve() {
var dfd = $.Deferred();
setTimeout(function() { dfd.resolve(); }, 1000);
return dfd.promise();
}
// en retournant le deferred l'utilisateur pourra le "resolve" ou "reject" (mauvaise idée) :
function publicResolve() {
var dfd = $.Deferred();
setTimeout(function() { dfd.resolve(); }, 1000);
return dfd;
}
David Soyez / Développeur Web
La méthode $.when()
jQuery.when( deferreds )
Returns: Promise
Exécute des fonctions callbacks basé sur un ou plusieurs deferred passées en paramètre.
Si une fonction sans deferred est passé en paramètre alors elle sera considérée comme déjà resolved.
Dans le cas où plusieurs diferred sont passés, when créera un diferred master.
Attention when() récupère les deferred en valeur retour de vos méthodes, il ne faut donc pas oublier les () pour les exécuter.
function maFonctionAsync() {
var dfd = $.Deferred(); // on créé un deferred pour cette méthode
setTimeout(dfd.resolve, 1000); // on resolve le deferred
return dfd.promise(); // on créé et retourne une promise
}
$.when(maFonctionAsync(), maFonctionAsync()) // les deux fonctions sont appelées simultanément
.then(function() { // est éxécuté une fois que les deux deferred sont resolved
alert('promises resolved');
})
David Soyez / Développeur Web
Les méthodes done(), fail(), always()
deferred.done( doneCallbacks [, doneCallbacks ] )
Returns: Deferred
Ajoute des callbacks quand un deferred est "rejected"
deferred.fail( failCallbacks [, failCallbacks ] )
Returns: Deferred
Ajoute des callbacks quand un deferred est "resolved"
deferred.always( alwaysCallbacks [, alwaysCallbacks ] )
Returns: Deferred
Ajoute des callbacks quand un deferred est soit "resolved" ou "reject"
$.get( "test.php" ).always(function() {
alert( "$.get completed with success or error callback arguments" );
});
$.get( "test.php" )
.done(function() {
alert( "$.get succeeded" );
})
.fail(function() {
alert( "$.get failed!" );
});
David Soyez / Développeur Web
Les méthodes done(), fail(), always()
Méthodes jQuery internes compatibles avec done(), fail() et always():
$.when(
$( '#item-1' ).animate( { opacity: 1 }, 500 ).promise(),
$( '#item-2' ).animate( { marginLeft : "-100px" }, 500 ).promise(),
$( '#item-3' ).animate( { width: 0 }, 1000 ).promise()
).done( function(){
console.log( "All animations complete." );
}
David Soyez / Développeur Web
La méthode then()
Ajoute des callbacks à appeler quand un deferred est "resolved", "rejected" ou toujours en progression
deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
Returns: Promise
function maFonctionAsync() {
var dfd = $.Deferred(); // on créé un deferred pour cette méthode
setTimeout(function() {
Math.random() < 0.5 ? dfd.reject() : dfd.resolve();
}, 500); // on resolve le deferred
return dfd.promise(); // on créé et retourne une promise
}
$.when(maFonctionAsync())
.done(function() {
alert('promise resolved');
})
.fail(function() {
alert('promise rejected');
})
.always(function() {
alert('promise always');
})
.then(function() {
alert('resolved, then do this now');
})
.then(function() {
alert('and that');
})
David Soyez / Développeur Web
La méthode then()
function maFonctionAsync() {
var dfd = $.Deferred(); // on créé un deferred pour cette méthode
setTimeout(function() {
Math.random() < 0.5 ? dfd.reject() : dfd.resolve();
}, 500); // on resolve le deferred
return dfd.promise(); // on créé et retourne une promise
}
$.when(maFonctionAsync())
.then(function() {
alert('promise resolved'); // resolved
},function() {
alert('promise rejected'); // rejected
})
.always(function() {
alert('promise always');
})
.then(function() {
alert('resolved, then do this now');
})
.then(function() {
alert('and that');
})
L'exemple précédent peut aussi s'écrire:
David Soyez / Développeur Web
Les notifications avec progress() et notify()
function getTemplate() {
var dfd = $.Deferred();
// notifie le deferred de la progression
dfd.notify('2% - Télécharge le template');
$.ajax( "/echo/html/", {
data: {
html: '<div id="template" style="padding-left:10px;color:red;"></div>',
delay: 1
},
method: 'POST'
}).done(function(html) {
// notifie le deferred de la progression
dfd.notify('50% - Template téléchargé');
// ajax terminé, resolve le deferred
dfd.resolve(html);
}).fail(function() {
// notifie le deferred de la progression
dfd.notify('erreur getTemplate');
// erreur ajax, reject le deferred
dfd.reject();
});
return dfd.promise();
}
function getData() {
var dfd = $.Deferred();
dfd.notify('51% - Télécharge les données');
$.ajax( "/echo/json/", {
data: {
json: JSON.stringify({value:new Date().getSeconds()}),
delay: 1
},
method: 'POST'
}
)
.done(function(data) {
// traitement des données
var result = parseInt(data.value) + parseInt($('#value').val());
// notifie le deferred de la progression
dfd.notify('100% - Données téléchargées');
// ajax terminé, resolve le deferred
dfd.resolve(result);
} ).fail(function() {
// notifie le deferred de la progression
dfd.notify('erreur getData');
// erreur ajax, reject le deferred
dfd.reject();
});
return dfd.promise();
}
jQuery('button').on('click', function() {
$.when(getTemplate())
.then(function(html) { // template téléchargé
$('body').append(html); // insére le template dans le dom
})
.then(getData)
.then(function(data) { // données téléchargées
$('#template').html(data); // insére les données dans le dom
})
.progress(function(msg) { // reçoit toutes les progressions
$('#statut').html(msg); // affiche le message de progression
});
});
David Soyez / Développeur Web
Conclusion
L'utilisation de deferred jQuery est un très bon choix lorsque votre application commence à avoir plusieurs imbrications de callbacks asynchrones.
Ils facilitent l'écriture et la lecture du chainage de fonctions asynchrone.
Ils permettent une gestion des erreurs plus facile.