2012-03-27 14 views
17

Tengo una función en mi aplicación nodejs llamada get_source_at. Toma un uri como argumento y su propósito es devolver el código fuente de ese uri. Mi problema es que no sé cómo hacer que la función invoque sincrónicamente solicitud, en lugar de darle esa función de devolución de llamada. I quiero flujo de control para detener durante los pocos segundos que tarda en cargar el uri. ¿Cómo puedo conseguir esto?¿Cómo puedo hacer que esta llamada para solicitar en nodejs sea sincrónica?

function get_source_at(uri){ 
    var source; 
    request({ uri:uri}, function (error, response, body) { 
     console.log(body); 
    }); 
    return source; 
} 

Además, he leído acerca de 'eventos' y como nodo es 'evented' y yo debería respetar que al escribir mi código. Me complace hacerlo, pero tengo que asegurarme de tener el código fuente de un uri antes de continuar con el flujo de control de mi aplicación, así que si no es haciendo la función sincrónica, ¿cómo se puede hacer? ?

+0

Si bien la respuesta de Lai es técnicamente correcta (así que la he marcado como respuesta), mi solución final necesitaba más trabajo. Cuando tiene una cantidad indeterminada de llamadas para solicitar puede usar el patrón en el código de muestra que se encuentra en https://gist.github.com/2234466 – Trindaz

Respuesta

16

Debe evitar solicitudes sincrónicas. Si desea algo como flujo de control sincrónico, puede usar async.

async.waterfall([ 
    function(callback){ 
     data = get_source_at(uri); 
     callback(null, data); 
    }, 
    function(data,callback){ 
     process(data, callback); 
    }, 
], function (err,result) { 
    console.log(result) 
}); 

El process se promete para ser ejecutado después de get_source_at devoluciones.

+16

"Debe evitar las solicitudes sincrónicas" no es una declaración general precisa. Debe evitar las solicitudes sincrónicas realizadas en el hilo principal de una aplicación o, en caso contrario, en situaciones que puedan bloquear su interfaz de usuario. Pero en el caso de, digamos, ¿un servicio web sin cabeza cuyo único propósito es delegar en algún otro servicio web e informar el resultado? Allí, una solicitud sincrónica está perfectamente bien y, de hecho, es preferible/limpiadora a una asíncrona. Es importante entender su caso de uso y elegir la solución que mejor se adapte. No es un caso de siempre uno y nunca el otro. – aroth

+1

Estoy de acuerdo con @aroth: estoy usando los scripts de nodejs para tareas de implementación complejas personalizadas donde necesito que cada comando se ejecute en una secuencia estricta. Podría usar promesas, pero incluso 'then()' encadenamiento puede ser algo feo si tienes más de 10 tareas para ejecutar en secuencia estricta. – JustAMartin

+0

Esa no es una respuesta a esta pregunta. –

0

Ok, primero que todo, para mantener el código asincrónico simplemente puede colocar el código relevante dentro de la devolución de llamada de la función de solicitud, lo que significa que se ejecutará después de que la solicitud finalice, pero no impida que el procesador maneje otras tareas en su aplicación . Si lo necesita varias veces, le aconsejaría que consulte Synchronous request in Node.js, que describe varios métodos para simplificarlo y analiza varias bibliotecas de flujo de control.

1

Tengo que asegurarme de que tengo el código fuente de un uri antes de continuar el flujo de control de mi aplicación, de modo que si no es haciendo la función síncrona, ¿cómo se puede hacer?

Teniendo en cuenta este punto de entrada a su aplicación:

function app(body) { 
    // Doing lots of rad stuff 
} 

Patea fuera por ir a buscar el cuerpo:

request({ uri: uri }, function (error, response, body) { 
    if(err) return console.error(err); 

    // Start application 
    app(body); 
} 

Esto es algo que tendrá que acostumbrarse a la hora de programar para node.js (y javascript en general). Hay módulos de flujo de control como async (que también yo recomiendo), pero tienes que acostumbrarte a continuar pasando el estilo, como se llama.

14

Usted puede con deasync:

function get_source_at(uri){ 
    var source; 
    request({ uri:uri}, function (error, response, body) { 
     source = body; 
     console.log(body); 
    }); 
    while(source === undefined) { 
     require('deasync').runLoopOnce(); 
    } 
    return source; 
} 
8

Esto es mejor manera de usar deasync.

var request = require("request") 
var deasync = require("deasync") 

var getHtml = deasync(function (url, cb) { 
    var userAgent = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36"} 
    request({ 
     url: url, 
     headers: userAgent 
    }, 
    function (err, resp, body) { 
     if (err) { cb(err, null) } 
     cb(null, body) 
    }) 
}) 

var title = /<title>(.*?)<\/title>/ 

var myTitle = getHtml("http://www.yahoo.com").match(title)[1] 
console.log(myTitle) 

favor refiérase a documentation of deasync, encontrará que puede utilizar
desync(function (n params, cb) {})
para hacer la función dedonde cb debe volver con (err, data). Así que las funciones similares a fs.readFile() se pueden envolver fácilmente con la función deasync. Pero para funciones como request que no vuelven con cb(err, data).Puede hacer su propia función (con nombre o anónimo) con un formato de devolución de llamada personalizado cb(err, data), tal como lo hice en el código anterior. De esta forma puede forzar que casi cualquier función asíncrona funcione como sincronización esperando a que la devolución de llamada cb(err, data) regrese a una capa de JavaScript diferente (como dice la documentación). También asegúrese de haber cubierto todas las formas de salir de la función que está ajustando con deasync con devoluciones de llamada cb(err, data), de lo contrario su programa se bloqueará.

Espero, ayuda a alguien por ahí!

Actualización:
No utilice esta forma de hacer peticiones síncronas. Use Async/Await para escribir promesas basadas en código de búsqueda sincrónica. Puede usar el módulo request-promise-native npm para evitar envolver el módulo de solicitudes con promesas usted mismo.

+1

Por favor haznos iluminar con tu conocimiento oculto. Exprese su razón para downvote en el comentario. ¡Gracias! –

+1

Esta es una buena solución. deasync es una manera mucho mejor y más elegante de hacer llamadas de sincronización. – Shad

1

¡Tener una función de bloqueo simple es una gran ayuda para el desarrollo interactivo! La función sync (definida a continuación) puede sincronizar cualquier promesa, reduciendo drásticamente la cantidad de sintaxis necesaria para jugar con una API y aprenderla. Por ejemplo, aquí es cómo usarlo con la librería puppeteer para Chrome sin cabeza:

var browser = sync(puppeteer.connect({ browserWSEndpoint: "ws://some-endpoint"})); 
var pages = sync(browser.pages()) 
pages.length 
1 
var page = pages[0] 
sync(page.goto('https://duckduckgo.com', {waitUntil: 'networkidle2'})) 
sync(page.pdf({path: 'webpage.pdf', format: 'A4'})) 

La mejor parte es, cada una de estas líneas puede ser ajustado hasta que lo haga lo que quiera, sin tener que volver a ejecutar o vuelva a escribir todas las líneas anteriores cada vez que quiera probarlo. Esto funciona porque tiene acceso directo a las variables browser y pages del nivel superior.

Así es como funciona:

const deasync = require("deasync"); 
const sync = deasync((promise, callback) => promise.then(result) => callback(null, result))); 

Se utiliza el paquete deasync mencionado en otras respuestas. deasync crea una aplicación parcial a la función anónima, que agrega callback como último argumento, y bloquea hasta que se llame al callback. callback recibe la condición de error como su primer argumento (si lo hay), y el resultado como su segundo (si lo hay).

+0

Esto es útil para versiones de nodos anteriores que no admiten async/await. –

Cuestiones relacionadas