¿Cómo espero que termine una promesa antes de devolver la variable de una función?

149

Todavía estoy luchando con las promesas, pero estoy progresando gracias a la comunidad aquí.

Tengo una función JS simple que consulta una base de datos Parse. Se supone que devuelve la matriz de resultados, pero obviamente debido a la naturaleza asincrónica de la consulta (de ahí las promesas), la función regresa antes que los resultados, dejándome con una matriz indefinida.

¿Qué debo hacer para que esta función espere el resultado de la promesa?

Aquí está mi código:

function resultsByName(name)
{   
    var Card = Parse.Object.extend("Card");
    var query = new Parse.Query(Card);
    query.equalTo("name", name.toString());

    var resultsArray = [];

    var promise = query.find({
               success: function(results) {
               // results is an array of Parse.Object.
                             console.log(results);
                             //resultsArray = results;
                             return results;
               },

               error: function(error) {
               // error is an instance of Parse.Error.
                             console.log("Error");
               }
    });                           

}
mac_55
fuente
3
También puede considerar usar async / await. Node ahora es compatible con async / await desde la versión 7.6
Viliam Simko

Respuestas:

66

En lugar de devolver un resultsArray, devuelve una promesa para una matriz de resultados y luego theneso en el sitio de la llamada: esto tiene el beneficio adicional de que la persona que llama sabe que la función está realizando E / S asincrónicas. La concurrencia de codificación en JavaScript se basa en eso: es posible que desee leer esta pregunta para tener una idea más amplia:

function resultsByName(name)
{   
    var Card = Parse.Object.extend("Card");
    var query = new Parse.Query(Card);
    query.equalTo("name", name.toString());

    var resultsArray = [];

    return query.find({});                           

}

// later
resultsByName("Some Name").then(function(results){
    // access results here by chaining to the returned promise
});

Puede ver más ejemplos de uso de promesas de análisis con consultas en la propia publicación de blog de Parse al respecto .

Benjamin Gruenbaum
fuente
¿Me puede decir cuál es el apoyo de esto? ¿IE9 apoya esto?
sandrina-p
Sí, pero Parse en sí está mayormente muerto, así que está esa @SandrinaPereira. Este es el código de análisis de la nube .
Benjamin Gruenbaum
1
Ah, ¿entonces esto no es solo javascript puro? Yo estaba buscando una manera de hacer esto (espera para una función para terminar de poner en marcha otro), pero sólo con javascript puro ..
sandrina-p
La pregunta es sobre el código de análisis, no las promesas. Las promesas pueden funcionar (con una biblioteca) en cualquier navegador. Bluebird se ejecuta en IE6 y netscape 7.
Benjamin Gruenbaum
1
He estado leyendo SO durante dos días, y aún así, nadie ha resuelto esto. Esta respuesta aceptada es la misma que cualquier otra. La función devuelve una Promesa, no un valor como lo solicitó el OP. ¿Por qué esta respuesta está marcada como aceptada?
iGanja
19

¿Qué debo hacer para que esta función espere el resultado de la promesa?

Uso async/await(NO forma parte de ECMA6, pero está disponible para Chrome, Edge, Firefox y Safari desde finales de 2017, consulte canIuse )
MDN

    async function waitForPromise() {
        // let result = await any Promise, like:
        let result = await Promise.resolve('this is a sample promise');
    }

Agregado debido al comentario: una función asíncrona siempre devuelve una Promesa, y en TypeScript se vería así:

    async function waitForPromise(): Promise<string> {
        // let result = await any Promise, like:
        let result = await Promise.resolve('this is a sample promise');
    }
Martin Meeser
fuente
44
La función asíncrona aún devolverá un objeto de promesa si se llama sin esperar (o en un código no asíncrono). Verifique el resultado de console.log (waitForPromise ()) si no está seguro. Una verificación de console.log (resultado) dentro de la función asíncrona se imprima lo que se espera, pero el retorno de la función asincrónico que sucede inmediatamente sin bloqueo y vuelve una promesa. El bloqueo en javascript suele ser muy malo, ya que es una aplicación de un solo subproceso y el bloqueo privará a cualquier otro pub / sub cliente de notificaciones, lo que esencialmente pondrá de rodillas a toda la aplicación.
SRM
1
.net tiene .wait () en la "promesa" como clase de tarea. ¿A Javascript le falta esta característica? Necesito esperar algo antes de salir de mi herramienta de línea de comandos de nodo que puede canalizar su salida a otra herramienta. "esperar" solo funciona dentro de las funciones asíncronas. Lo que significa que no funciona fuera del alcance de una promesa.
TamusJRoyce
@SRM Siento que su comentario se basa en una mala interpretación de la muestra: se trata de la Promise.resolve "interna" (como el ejemplo de Promise más simple), por lo que no hay una persona que llama externamente como está indicando en su comentario. Entonces decidí actualizar la respuesta.
Martin Meeser el
@TamusJRoyce Supongo que es una pregunta en sí misma, pero creo que en C # puedes usar Task.ContinueWith (Task), que es la misma idea que ves en la respuesta aceptada (donde se llama "then ()").
Martin Meeser el
Creo que veo ahora. Puedo envolver todo mi script en una gran función asíncrona. Y llame a esa función como la última línea de mi script. Esa función aún sería un poco de placa de caldera. Pero mucho menos de lo que previamente percibí. No estoy acostumbrado a escribir funciones dentro de funciones. ¡Gracias @MartinMeeser!
TamusJRoyce
3

No desea hacer que la función espere, porque JavaScript está destinado a no ser bloqueante. En lugar de devolver la promesa al final de la función, la función de llamada puede usar la promesa para obtener la respuesta del servidor.

var promise = query.find(); 
return promise; 

//Or return query.find(); 
Rastro
fuente
1
Todo tu asunto de devoluciones de llamada con el success:bit está apagado.
Benjamin Gruenbaum
O mejor: return query.find();.
puré
También está bien. Lo dejaré así con fines ilustrativos, pero lo agregué como comentario.
Rastrear el
Intenté esto pero los resultados parecen ser indefinidos. resultsByName ("name"). then (function (results) {console.log ("got array" + results.count);});
mac_55
1
Gracias, debe haber habido algún tipo de error dentro de la función de resultados. Está funcionando ahora. Cambié mi console.log a results.length y puedo ver que hay 1 entrada en mi matriz devuelta :)
mac_55
2

En realidad no estás usando promesas aquí. Parse le permite usar devoluciones de llamada o promesas; tu elección.

Para usar promesas, haga lo siguiente:

query.find().then(function() {
    console.log("success!");
}, function() {
    console.log("error");
});

Ahora, para ejecutar cosas después de que se complete la promesa, puede ejecutarla dentro de la devolución de llamada de la promesa then() llamada. Hasta ahora, esto sería exactamente lo mismo que las devoluciones de llamada regulares.

Hacer un buen uso de las promesas es cuando las encadena, así:

query.find().then(function() {
    console.log("success!");

    return new Parse.Query(Obj).get("sOmE_oBjEcT");
}, function() {
    console.log("error");
}).then(function() {
    console.log("success on second callback!");
}, function() {
    console.log("error on second callback");
});
mezcla
fuente
¿Qué tipo de objeto es el objeto de resultados? ya que no parece contener mi matriz
mac_55
Debería ser una serie de Parse.Object's.
puré