jQuery: devuelve datos después del éxito de la llamada ajax [duplicado]

506

Tengo algo como esto, donde es una simple llamada a un script que me devuelve un valor, una cadena ...

function testAjax() {
    $.ajax({
      url: "getvalue.php",  
      success: function(data) {
         return data; 
      }
   });
}

pero si llamo a algo como esto

var output = testAjax(svar);  // output will be undefined...

entonces, ¿cómo puedo devolver el valor? el siguiente código tampoco parece funcionar ...

function testAjax() {
    $.ajax({
      url: "getvalue.php",  
      success: function(data) {

      }
   });
   return data; 
}
2
430

Nota: Esta respuesta se escribió en febrero de 2010.
Consulte las actualizaciones de 2015, 2016 y 2017 en la parte inferior.

No puede devolver nada de una función que sea asincrónica. Lo que puede devolver es una promesa . Expliqué cómo funcionan las promesas en jQuery en mis respuestas a esas preguntas:

Si pudiera explicar por qué desea devolver los datos y qué desea hacer con ellos más adelante, entonces podría darle una respuesta más específica sobre cómo hacerlo.

Generalmente, en lugar de:

function testAjax() {
  $.ajax({
    url: "getvalue.php",  
    success: function(data) {
      return data; 
    }
  });
}

puedes escribir tu función testAjax así:

function testAjax() {
  return $.ajax({
      url: "getvalue.php"
  });
}

Entonces puede obtener su promesa de esta manera:

var promise = testAjax();

Puede almacenar su promesa, puede pasarla, puede usarla como un argumento en llamadas a funciones y puede devolverla desde funciones, pero cuando finalmente desee usar sus datos devueltos por la llamada AJAX, debe Hazlo asi:

promise.success(function (data) {
  alert(data);
});

(Consulte las actualizaciones a continuación para obtener una sintaxis simplificada).

Si sus datos están disponibles en este momento, esta función se invocará inmediatamente. Si no es así, se invocará tan pronto como los datos estén disponibles.

El objetivo de hacer todo esto es que sus datos no están disponibles inmediatamente después de la llamada a $ .ajax porque son asincrónicos. Promises es una buena abstracción para que las funciones digan: No puedo devolverte los datos porque aún no los tengo y no quiero bloquear y hacerte esperar, así que aquí tienes una promesa y podrás úselo más tarde, o simplemente para dárselo a otra persona y terminar con él.

Vea esta DEMO .

ACTUALIZACIÓN (2015)

Actualmente (a marzo de 2015) jQuery Promises no es compatible con la especificación Promises / A +, lo que significa que es posible que no cooperen muy bien con otras implementaciones compatibles con Promises / A + .

Sin embargo, jQuery Promises en la próxima versión 3.x será compatible con la especificación Promises / A + (gracias a Benjamin Gruenbaum por señalarlo). Actualmente (a mayo de 2015) las versiones estables de jQuery son 1.xy 2.x.

Lo que expliqué anteriormente (en marzo de 2011) es una forma de usar jQuery Deferred Objects para hacer algo de forma asincrónica que en el código síncrono se lograría al devolver un valor.

Pero una llamada de función síncrona puede hacer dos cosas: puede devolver un valor (si puede) o lanzar una excepción (si no puede devolver un valor). Promises / A + aborda ambos casos de uso de una manera que es casi tan poderosa como el manejo de excepciones en código síncrono. La versión de jQuery maneja el equivalente a devolver un valor muy bien, pero el equivalente al manejo de excepciones complejas es algo problemático.

En particular, el objetivo del manejo de excepciones en el código síncrono no es solo rendirse con un mensaje agradable, sino intentar solucionar el problema y continuar la ejecución, o posiblemente volver a generar la misma excepción o una diferente para algunas otras partes del programa. resolver. En código síncrono tienes una pila de llamadas. En la llamada asincrónica no lo hace y el manejo avanzado de excepciones dentro de sus promesas como lo requiere la especificación Promises / A + realmente puede ayudarlo a escribir código que manejará errores y excepciones de una manera significativa incluso para casos de uso complejos.

Para conocer las diferencias entre jQuery y otras implementaciones, y cómo convertir las promesas de jQuery en compatibles con Promises / A +, consulte Viniendo de jQuery por Kris Kowal et al. en la wiki de la biblioteca Q y Promises llegan en JavaScript por Jake Archibald en HTML5 Rocks.

Cómo devolver una promesa real

La función de mi ejemplo anterior:

function testAjax() {
  return $.ajax({
      url: "getvalue.php"
  });
}

devuelve un objeto jqXHR, que es un objeto diferido jQuery .

Para que devuelva una promesa real, puede cambiarlo a, utilizando el método de Q wiki :

function testAjax() {
  return Q($.ajax({
      url: "getvalue.php"
  }));
}

o, utilizando el método del artículo HTML5 Rocks :

function testAjax() {
  return Promise.resolve($.ajax({
      url: "getvalue.php"
  }));
}

Esto Promise.resolve($.ajax(...))también es lo que se explica en la promisedocumentación del módulo y debería funcionar con ES6Promise.resolve() .

Para usar ES6 Promises hoy, puede usar el módulo es6-promise depolyfill() Jake Archibald.

Para ver dónde puede usar ES6 Promises sin el polyfill, consulte: ¿Puedo usar: Promesas ?

Para obtener más información, consulte:

Futuro de jQuery

Las versiones futuras de jQuery (a partir de 3.x; las versiones estables actuales a mayo de 2015 son 1.xy 2.x) serán compatibles con la especificación Promises / A + (gracias a Benjamin Gruenbaum por señalarlo en los comentarios). "Dos cambios que ya hemos decidido son la compatibilidad Promise / A + para nuestra implementación diferida [...]" ( jQuery 3.0 y el futuro del desarrollo web ). Para obtener más información, consulte: jQuery 3.0: Las próximas generaciones de Dave Methvin y jQuery 3.0: Más interoperabilidad, menos Internet Explorer de Paul Krill.

Charlas interesantes

ACTUALIZACIÓN (2016)

Hay una nueva sintaxis en ECMA-262, sexta edición, sección 14.2 llamada funciones de flecha que se pueden usar para simplificar aún más los ejemplos anteriores.

Usando la API de jQuery, en lugar de:

promise.success(function (data) {
  alert(data);
});

puedes escribir:

promise.success(data => alert(data));

o usando la API Promises / A +:

promise.then(data => alert(data));

Recuerde siempre usar controladores de rechazo con:

promise.then(data => alert(data), error => alert(error));

o con:

promise.then(data => alert(data)).catch(error => alert(error));

Vea esta respuesta para ver por qué siempre debe usar controladores de rechazo con promesas:

Por supuesto, en este ejemplo, podría usar solo promise.then(alert)porque está llamando alertcon los mismos argumentos que su devolución de llamada, pero la sintaxis de flecha es más general y le permite escribir cosas como:

promise.then(data => alert("x is " + data.x));

No todos los navegadores admiten esta sintaxis todavía, pero hay ciertos casos en los que está seguro de en qué navegador se ejecutará su código, por ejemplo, al escribir una extensión de Chrome , un complemento de Firefox o una aplicación de escritorio usando Electron, NW.js o AppJS (consulte esta respuesta para obtener más detalles).

Para la compatibilidad con las funciones de flecha, consulte:

ACTUALIZACIÓN (2017)

Hay una sintaxis aún más nueva en este momento llamada funciones asíncronas con una nueva awaitpalabra clave que en lugar de este código:

functionReturningPromise()
    .then(data => console.log('Data:', data))
    .catch(error => console.log('Error:', error));

te permite escribir:

try {
    let data = await functionReturningPromise();
    console.log('Data:', data);
} catch (error) {
    console.log('Error:', error);
}

Solo puede usarlo dentro de una función creada con la asyncpalabra clave. Para obtener más información, consulte:

Para obtener soporte en navegadores, consulte:

Para obtener soporte en Node, consulte:

En lugares donde no tiene soporte nativo asyncy awaitpuede usar Babel:

o con una sintaxis ligeramente diferente, un enfoque basado en un generador como en las cocorrutinas de Bluebird:

Más información

Algunas otras preguntas sobre promesas para obtener más detalles:

9
  • Solo están jugando con ajax, viendo cómo funciona.
    Techism
    15/03/11 a las 19:21
  • 6
    ¡Respuesta perfecta! Solo para agregar una nota al margen para los usuarios, esto no funcionará con jQuery ver 1.4.
    Trevor
    1/04/11 a las 14:41
  • 9
    Esto ayudó mucho. Solo salté el paso var promise = testAjax()e hice esto testAjax().success(function (data) { alert(data); }); 11/07/12 a las 19:55
  • 1
    @AlexG En lugar de promise.success(function (data) { alert(data); });en mi ejemplo, puede usar promise.success(function (data) { alert(data.users[0].id); alert(data.prices[x]); });o algo así. Si obtiene los datos en la successdevolución de llamada (o thendevolución de llamada, si está utilizando Promises / A + API), obtendrá los datos con todas sus propiedades.
    rsp
    27/06/2016 a las 6:27
  • 3
    Es encantador cuando la forma de hacer algo cambia cada año.
    Matt
    27 de septiembre de 2018 a las 10:18
418

La única forma de devolver los datos de la función sería realizar una llamada sincrónica en lugar de una llamada asincrónica, pero eso congelaría el navegador mientras espera la respuesta.

Puede pasar una función de devolución de llamada que maneja el resultado:

function testAjax(handleData) {
  $.ajax({
    url:"getvalue.php",  
    success:function(data) {
      handleData(data); 
    }
  });
}

Llámalo así:

testAjax(function(output){
  // here you use the output
});
// Note: the call won't wait for the result,
// so it will continue with the code here while waiting.
8
  • Supongo que agregar el estado [éxito] retrasaría un poco y sería más preciso llamar solo cuando el estado es 400 exitoso.
    meYnot
    6 de enero de 2016 a las 17:03
  • 1
    @iamsirjayesh ¡Haré matemáticas por ti, solo 5,5 años! ... sin embargo útil respuesta. 28 de junio de 2017 a las 14:06
  • 12
    successy errorestán siendo obsoletos en jQuery 1.8. Deberías empezar a usar .done()y .fail(). Consulte la documentación . 7/07/2017 a las 20:45
  • 3
    Lo que está en desuso son las funciones de manipulación de devolución de llamada (por ejemplo, .error, .success), no los parámetros del método ajax. Vea los comentarios en este hilo. stackoverflow.com/a/10931891/4490454
    EGS
    2 de enero de 2018 a las 9:21
  • 1
    @Mike: Lo que pasa es que no puedes obtener el resultado de la llamada a menos que tengas una TARDIS. Como la llamada es asincrónica, el resultado llegará después de que regrese la llamada. Puede considerar el uso de promesas proporcionadas en otras respuestas aquí, lo que le permitirá devolver algo de la llamada aunque el resultado aún no haya llegado. En algún momento tienes que esperar el resultado de todos modos, una promesa te permite hacerlo en una parte diferente del código.
    Guffa
    3/08/19 a las 22:42
205

puede agregar la opción async a false y regresar fuera de la llamada ajax.

function testAjax() {
    var result="";
    $.ajax({
      url:"getvalue.php",
      async: false,  
      success:function(data) {
         result = data; 
      }
   });
   return result;
}
9
  • 17
    Tu solución es perfectamente válida. Solo quiero enfatizar la importancia de no devolver el valor de inmediato dentro de la devolución de llamada exitosa, sino fuera de la llamada a la función .ajax. De lo contrario, quedará indefinido. 11/07/2013 a las 11:56
  • 2
    ¿Hay alguna forma de usar esta función con async: true?
    vivex
    14 de marzo de 2015 a las 8:45
  • dieciséis
    async: falseahora está en desuso en la especificación whatwg para la mayoría de los casos de uso. Google Chrome ya advierte sobre esto en su consola cuando se produce una llamada con async: false. La especificación w3c no parece haberla desaprobado todavía. 30/04/15 a las 19:45
  • ¿Por qué no funciona esto? función get_cmd_info3 (cmd) {var result = null; $ .get ("bash /" + cmd, function (data) {console.log (data); result = data}, 'json'); devolver resultado; } 20/04/2016 a las 14:27
  • esto no funcionó para mi ... ¡el resultado viene como indefinido en mi declaración de devolución! 11/08/2016 a las 8:22
0

Idk si ustedes lo resolvieron, pero recomiendo otra forma de hacerlo, y funciona :)

    ServiceUtil = ig.Class.extend({
        base_url : 'someurl',

        sendRequest: function(request)
        {
            var url = this.base_url + request;
            var requestVar = new XMLHttpRequest();
            dataGet = false;

            $.ajax({
                url: url,
                async: false,
                type: "get",
                success: function(data){
                    ServiceUtil.objDataReturned = data;
                }
            });
            return ServiceUtil.objDataReturned;                
        }
    })

Entonces, la idea principal aquí es que, al agregar async: false, hace que todo espere hasta que se recuperen los datos. Luego lo asigna a una variable estática de la clase, y todo funciona mágicamente :)

1
-15

Vea el ejemplo de jquery docs: http://api.jquery.com/jQuery.ajax/ (aproximadamente 2/3 de la página)

Es posible que esté buscando el siguiente código:

    $.ajax({
     url: 'ajax/test.html',
     success: function(data) {
     $('.result').html(data);
     alert('Load was performed.');
   }
});

Misma página ... más abajo.

1
  • NO ¿Y si el resultado es necesario en un procesamiento posterior dentro de otra función ?, ¿cómo lo vas a pasar ahí? 3 de agosto a las 7:38