Introduction aux

Objets différés

David Soyez / Développeur Web

Introduction aux

Objets différés

Sommaire

  • Compatibilité
  • Avantages
  • Quand les utiliser?
  • L'objet Deferred
  • L'objet Promise
  • Deferred vs. Promise
  • La méthode $.when()
  • Les méthodes done(), fail() et always
  • La méthode then()
  • Les notifications avec progress() et notify()
  • Conclusion

David Soyez / Développeur Web

Introduction aux

Objets différés

  • jQuery >= 1.5
  • ECMAScript 6 (pour les promises)

David Soyez / Développeur Web

Compatibilité

Introduction aux

Objets différés

  • Relayer les états (succès ou erreurs) d'une pile de callbacks synchrones ou asynchrones
  • Sépare l'appel de la résolution des méthodes
  • Evite l'imbrication de callbacks
  • Ecriture des appels de méthodes asynchrones plus claires
  • Abstraction du code facilitant la lecture et la réutilisation

David Soyez / Développeur Web

...ok, mais concrètement ça donne quoi?

Avantages

Introduction aux

Objets différés

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);
            });
        } 
    });    

});

Introduction aux

Objets différés

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);
    });

});

Introduction aux

Objets différés

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);
});

Introduction aux

Objets différés

David Soyez / Développeur Web

Quand les utiliser?

  • Appels Ajax
  • Chargement de ressources
  • setTimeout(), setInterval()
  • Animations
  • Interactions utilisateur

Introduction aux

Objets différés

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:

  • resolve, reject

et comprend 5 méthodes par lequel attacher des callbacks:

  • always, done, fail, progress, then

Introduction aux

Objets différés

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:

Introduction aux

Objets différés

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;
}

Introduction aux

Objets différés

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');
})

Introduction aux

Objets différés

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!" );
  });

Introduction aux

Objets différés

David Soyez / Développeur Web

Les méthodes done(), fail(), always()

Méthodes jQuery internes compatibles avec done(), fail() et always():

  • jQuery.ajax() +then()
  • jQuery.get() +then()
  • jQuery.post() +then()
  • jQuery.getJSON() +then()
  • jQuery.getScript() +then()
  • .slideUp() +progress()
  • .slideDown() +progress()
  • .slideToggle() +progress()
  • .animate() +progress()
  • .fadeIn() +progress()
  • .fadeOut() +progress()
$.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." );
}

Introduction aux

Objets différés

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');
})

Introduction aux

Objets différés

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:

Introduction aux

Objets différés

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
    });

});

Introduction aux

Objets différés

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.

Introduction aux jQuery Deferred Objects

By David Soyez

Introduction aux jQuery Deferred Objects

  • 1,029