web-dev-qa-db-fra.com

Retour promesse déjà résolue

J'ai un projet existant qui a beaucoup de fonctions asynchrones qui renvoient des promesses. J'ajoute de la mise en cache afin que dans certains cas, les fonctions asynchrones se terminent de manière synchrone et souhaitent rendre ce code plus court/meilleur si possible:

        return $.Deferred(function(def) { def.resolve(); }).promise();

Par exemple, j'ai une classe Data Service qui gère la plupart des AJAX requêtes qui ressemblent à ceci:

function DataService() {

    var self = this;

    self.makeRequest = function(functionName, data) {
        return $.Deferred(function(def) {

            var jsonData = JSON.stringify(data);

            $.ajax({
                type: "POST",
                url: "WebService.asmx/" + functionName,
                data: jsonData,
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                error: function(xhr, status, error) {
                    var ex;
                    try {
                        ex = eval("(" + xhr.responseText + ")");
                        ex.message = ex.Message;
                        ex.Message = undefined;
                    } catch (ex2) {
                        ex = { message: "Invalid Response From Server:\r\n" + xhr.responseText };
                    }
                    if (ex.message == "LoginRequired") {
                        app.viewModels.main.loginRequired(true);
                    }
                    else {
                        app.showError(ex.message);
                    }
                    def.reject(ex.message);
                }
            });
        }).promise();
    }
}

Ensuite, j'ai une fonction dans une autre classe qui appelle actuellement toujours makeRequest:

self.deleteMe = function()
{
   return app.dataservice.makeRequest('deleteItem');
}

Je veux mettre à jour la fonction deleteMe afin qu'elle n'appelle pas toujours makeRequest, et au lieu de cela, elle effectue simplement un travail synchrone, puis revient. Il doit cependant renvoyer une promesse, car quel que soit le nom, il s'y attendra, mais il doit s'agir d'une "promesse déjà terminée/résolue". Actuellement, j'utilise le premier ensemble de codes ci-dessus pour ce faire. Il semble qu'il doit y avoir un meilleur moyen.

43
eselk

@Eselk,

D'après mon expérience, la construction $.Deferred(function(def) {...}); est rarement nécessaire, même si je suppose qu'elle peut être très utile dans certaines circonstances.

D'abord, :

return $.Deferred(function(def) { def.resolve(); }).promise();

simplifiera pour:

return $.Deferred().resolve().promise();

Deuxièmement, dans DataService.makeRequest() vous pouvez éviter d'avoir besoin d'un $.Deferred Explicite en exploitant .then(), comme suit:

function DataService() {
    var self = this;
    self.makeRequest = function (functionName, data) {
        return $.ajax({
            type: "POST",
            url: "WebService.asmx/" + functionName,
            data: JSON.stringify(data),
            contentType: "application/json; charset=utf-8",
            dataType: "json"
        }).then(null, function (xhr, status, error) {
            var ex;
            try {
                ex = eval("(" + xhr.responseText + ")");
                ex.message = ex.Message;
                ex.Message = undefined;
            } catch (ex2) {
                ex = {
                    message: "Invalid Response From Server:\r\n" + xhr.responseText
                };
            }
            if (ex.message == "LoginRequired") {
                app.viewModels.main.loginRequired(true);
            } else {
                app.showError(ex.message);
            }

            return ex.message;
        });
    };
}

Les aspects clés ici sont:

  • $.ajax() renvoie un objet jqXHR compatible avec les promesses, qui (en cas de succès/erreur) est immédiatement traité et modifié par .then().
  • .then(null, ...) - provoque la transmission d'une nouvelle promesse, résolue avec les mêmes valeurs que la promesse d'origine renvoyée par $.ajax(). Ainsi, sous la condition 'done' (ie ajax success), .then() est complètement transparent.
  • return ex.message; - fait passer une nouvelle promesse, rejetée avec ex.message.

L'effet net doit cependant être identique à votre code d'origine, à mon humble avis, chaîner .then() est beaucoup plus propre que de tout configurer dans un rappel $.Deferred().

Soit dit en passant, vous pourrez peut-être éviter la nécessité de eval("(" + xhr.responseText + ")") etc. en définissant un en-tête HTTP approprié côté serveur de sorte que votre '.Message' apparaisse directement comme l'argument status (ou xhr.status?) Du rappel échoué. Par exemple, en PHP vous feriez quelque chose comme:

$message = "my custom message";
header("HTTP/1.1 421 $message");
exit(0);

Je suis sûr que ASP offre la même capacité.

IIRC, tout code d'état de la série 4xx fera l'affaire. 421 se trouve être un sans une signification spécifique.

49

Utilisez simplement return $.when(); pour retourner une promesse déjà résolue.

Si vous ne lui passez aucun argument, jQuery.when () renverra une promesse résolue.

Référence: https://api.jquery.com/jquery.when/


Remarques:

  • C'est la même chose que return $.when(undefined); qui conduit à l'astuce plutôt cool suivante qui évite toute utilisation de tableaux et apply.

Si vous voulez attendre qu'un nombre variable de promesses se termine en parallèle alors vous pouvez utiliser ce modèle en boucle:

var promise;   // Undefined is treated as an initial resolved promise by $.when
while (insomeloop){
    promise = $.when(promise, newpromise);
}

puis faites un dernier appel à la fin avec:

promise.then(function(){
    // All done!
});

par exemple.

var promise;$.when
var $elements = $('.some-elements');
$elements.each(function(){
     var $element = $(this).fadeout();
     promise = $.when(promise, $element.promise());
});
promise.then(function(){
    // All promises completed!
});

Les inconvénients sont mineurs:

  • chaque appel à when encapsule la promesse précédente dans une nouvelle promesse. Un surcoût mineur et vous n'avez plus besoin de maintenir et d'évaluer un éventail de promesses.
  • aucune valeur ne peut être directement transmise à la fonction finale. Comme vous ne voulez généralement pas les valeurs de retour de promesses parallèles, c'est mineur.
  • l'échec d'une seule promesse annulera tôt l'attente finale, vous devez donc vous assurer que toutes les promesses seront tenues.
26
Gone Coding