2010-02-08 26 views
7

Quiero convertir una función asincrónica en síncrona.Programación asincrónica en JavaScript sin devoluciones de llamada desordenadas

function fetch() { 
    var result = 'snap!'; 
    $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", function messyCallback(data){ 
    result = data; 
    }); 
    return result; 
} 

document.write(fetch());​

See in action

El resultado será siempre 'no!', Porque $.getJSON fuga después de fetch() se hace.

Mi primera idea era:

function fetch() { 
    var result = 'snap!'; 
    $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", function messyCallback(data){ 
    result = data; 
    }); 
    while (true) { if (result != 'snap!') return result; } 
}

No funciona y también volar fuera del navegador.

He leído sobre generators and iterators in JS 1.7, pero no tengo idea de cómo aplicarlo a mi problema.

Esta pregunta no es realmente acerca de jQuery. En lugar de $.getJSON podría ser cualquier otra función asincrónica.

+0

posible (probable?) Duplicados - ver http://stackoverflow.com/questions/1455870/jquery-wait-for-function-to-complete -to-continuar-procesando. –

+0

Jeff, no lo creo. Uso la llamada JSONP, que siempre es sincrónica. – NVI

+1

@NV: JSONP solo es síncrono cuando se analiza como parte del documento HTML. Si se agrega a la página usando el DOM, es asíncrono. –

Respuesta

9

Ver esta pregunta también: Halt JavaScript execution without locking up the browser

Haciendo exactamente lo que quiere no funciona. No puede crear sincronicidad desde una función asíncrona (¡solo al revés!) En un entorno controlado por eventos de subproceso único. Debería adoptar la naturaleza asincrónica del lenguaje y desarrollar su propio estilo consistente en el manejo de devoluciones de llamadas para que su código sea legible/mantenible. Podría, por ejemplo, elegir nunca hacer un trabajo real dentro de un cierre de devolución de llamada, sino simplemente llamar a otra función/método de nivel superior para mayor claridad.

1

El nivel inferior $.ajax() function de jQuery tiene más opciones, incluido async: [true|false] (el valor predeterminado es verdadero).

Sin embargo, en la mayoría de los casos, debe seguir los consejos de Ben y "abrazar la naturaleza asincrónica del lenguaje".

+0

AFAIK, estas opciones no funcionan con llamadas JSONP. – NVI

+0

Drew: gracias por llamar a esto. Es verdad que JS tiene un pequeño número de llamadas (confusamente) sincrónicas, el 'get' de XMLHttpRequest es uno de ellos. –

+0

Quise decir 'async: false' no funciona con llamadas JSONP. – NVI

2

Quiere lanzar su propio $ .getSyncJSON que usa $ .ajax ({async: falso}). Ver: http://api.jquery.com/jQuery.ajax/.

Debo advertirle contra este curso de acción, sin embargo. Puede bloquear fácilmente el navegador desde todas las entradas, poniendo su interfaz de usuario a merced de la red. Pero si sabes lo que estás haciendo y estás seguro de tu caso de uso, adelante.

2

En lugar de escribir métodos auxiliares como su fetch que devuelve un valor, hacerles aceptar otra función, un "receptor", para pasar su resultado a:

function fetch(receiver) { 

    $.getJSON("blah...", function(data) { 

     receiver(data); 
    }); 
} 

Obviamente, esto es redundante porque es exactamente cómo getJSON ya funciona, pero en un ejemplo más realista, la función fetch procesará o filtrará el resultado de alguna manera antes de pasarlo.

Entonces, en lugar de:

document.write(fetch());​ 

Harías:

fetch(function(result) { document.write(result); }); 

Los generadores pueden ser utilizados para hacer un código asíncrono mucho más lineal en el estilo. Cada vez que necesitó algún resultado asíncrono, obtendría una función para iniciarlo, y el generador se reanudaría cuando el resultado estuviera disponible.Habría un poco de código de administración manteniendo el generador funcionando. Pero esto no es de mucha ayuda porque los generadores no son estándar en todos los navegadores.

Si te interesa, aquí tienes un blog post about using generators to tidy up asynchronous code.

+0

En su código, el receptor debe estar dentro de la función de devolución de llamada. – NVI

+0

@NV - oops, buena llamada. –

+0

He jugado con los generadores de Mozilla y me parecen excelentes. Es una pena que no sean ampliamente adoptados en otros lugares. Ver el enlace que agregué al final de la respuesta. –

2

Hay una extensión del lenguaje JavaScript llamada StratifiedJS. Se ejecuta en todos los navegadores y le permite hacer justamente eso: manejar problemas asincrónicos de forma sincrónica/lineal sin congelar su navegador.

Puede habilitar JavaScript estratificado p. Ej. mediante la inclusión de Oni Apolo en su página web como:

<script src="http://code.onilabs.com/latest/oni-apollo.js"></script> 
<script type="text/sjs"> your StratifiedJS code here </script> 

y su código se vería así:

function fetch() { 
    return require("http").jsonp(
    "http://api.flickr.com/services/feeds/photos_public.gne?" + 
    "tags=cat&tagmode=any&format=json", {cbfield:"jsoncallback"}); 
} 
document.write(fetch());​ 

O si realmente desea utilizar jQuery en StratifiedJS:

require("jquery-binding").install(); 
function fetch() { 
    var url = "http://api.flickr.com/?format=json&...&jsoncallback=?" 
    return $.$getJSON(url); 
} 
document.write(fetch());​ 

Los documentos están en http://onilabs.com/docs

2

La biblioteca TameJS está diseñada para este problema

Es posible escribir algo como (no probado):

var result = 'snap!'; 
await { 
    $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", defer(result)); 
} 
return result; 
Cuestiones relacionadas