Promesa de devolución ya resuelta

5 minutos de lectura

Tengo un proyecto existente que tiene muchas funciones asincrónicas que devuelven promesas. Estoy agregando algo de almacenamiento en caché para que, en algunos casos, las funciones asincrónicas se completen sincrónicamente y me gustaría acortar/mejorar este código si es posible:

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

Por ejemplo, tengo una clase de servicio de datos que maneja la mayoría de las solicitudes de AJAX que se ve así:

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

Luego tengo una función en otra clase que actualmente siempre llama a makeRequest:

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

Quiero actualizar la función deleteMe para que no siempre llame a makeRequest y, en su lugar, solo haga un trabajo síncrono y luego regrese. Sin embargo, debe devolver una promesa, porque lo que sea que lo haya llamado esperará eso, pero debe ser una “promesa ya completada/resuelta”. Actualmente estoy usando el primer conjunto de código anterior para hacer eso. Parece que debe haber una mejor manera.

avatar de usuario
Remolacha-Remolacha

@Eselk,

En mi experiencia, el $.Deferred(function(def) {...}); La construcción rara vez se necesita, aunque supongo que puede ser bastante útil en algunas circunstancias.

En primer lugar, :

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

se simplificará a:

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

En segundo lugar, en DataService.makeRequest() puede evitar la necesidad de un explícito $.Deferred al explotar .then()como sigue :

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

Los aspectos clave aquí son:

  • $.ajax() devuelve un objeto jqXHR compatible con la promesa, que (en caso de éxito/error) es inmediatamente modificado por .then().
  • .then(null, ...) – hace que se transmita una nueva promesa, resuelta con los mismos valores que la promesa original devuelta por $.ajax(). Por lo tanto, bajo la condición ‘hecho’ (es decir, éxito ajax), .then() es completamente transparente.
  • return ex.message; – hace que se transmita una nueva promesa, rechazada con ex.message.

Sin embargo, el efecto neto debería ser idéntico a su código original, en mi humilde opinión, encadenando .then() es significativamente más limpio que configurar todo dentro de un $.Deferred() llamar de vuelta.

Por cierto, es posible que pueda evitar la necesidad de eval("(" + xhr.responseText + ")") etc. configurando un encabezado HTTP apropiado en el lado del servidor de modo que su ‘.Message’ aparezca directamente como el status argumento (o xhr.status?) de la devolución de llamada fallida. Por ejemplo, en PHP harías algo como:

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

Estoy seguro de que ASP ofrece la misma capacidad.

IIRC, cualquier código de estado de la serie 4xx hará el trabajo. 421 resulta ser uno sin un significado específico.

  • Gracias por toda la información. Decidí que necesito que mis llamadas ajax se puedan cancelar en niveles más altos, por lo que estoy devolviendo diferidos en la mayoría de los lugares ahora, por lo que, según su respuesta, solo necesito $.Deferred.resolve(); — bonito y limpio.

    – eselk

    21 de mayo de 2013 a las 14:11

  • Creo que quieres decir $.Deferred().resolve().promise();

    – aheuermann

    15 de enero de 2014 a las 21:04

avatar de usuario
Codificación pasada

Simplemente use return $.when(); regresar una promesa ya resuelta.

Si no le pasa ningún argumento, jQuery.when() devolverá una promesa resuelta.

Referencia: https://api.jquery.com/jquery.when/


Notas:

  • esto es lo mismo que return $.when(undefined); lo que lleva al siguiente truco bastante genial que evita el uso de matrices y apply.

Si desea esperar a que se complete un número variable de promesas en paralelo entonces puedes usar este patrón en un bucle:

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

luego haga una llamada final al finalizar con:

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

p.ej

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

Las desventajas son menores:

  • cada llamada a when envuelve la promesa anterior en una nueva promesa. Una sobrecarga menor y ya no necesita mantener y evaluar una serie de promesas.
  • no se pueden pasar valores directamente a través de la función final. Como normalmente no desea que los valores de retorno de promesas paralelasesto es menor.
  • el fracaso de una sola promesa abortará la espera final antes de tiempo, por lo que debe asegurarse de que se cumplan todas las promesas.

  • Sin argumento no se resolverá, sin embargo, incluso undefined como argumento se resolverá. Extraño.

    – br4nnigan

    10 de septiembre de 2020 a las 13:22


¿Ha sido útil esta solución?