Comprender el código asincrónico en términos sencillos
Entiendo lo básico sobre la asincronía: las cosas no se ejecutan secuencialmente. Y entiendo que hay algo muy poderoso en eso... supuestamente. Pero por mi vida no puedo entender el código. Echemos un vistazo al código asíncrono de Node.JS que HE ESCRITO... pero que realmente no entiendo.
function newuser(response, postData) {
console.log("Request handler 'newuser' was called.");
var body = '<html>' +
'<head>' +
'<meta http-equiv="Content-Type" content="text/html; ' +
'charset=UTF-8" />' +
'</head>' +
'<body>' +
'<form action=" /thanks" method="post">' +
'<h1> First Name </h1>' +
'<textarea name="text" rows="1" cols="20"></textarea>' +
'<h1> Last Name </h1>' +
'<textarea name="text" rows="1" cols="20"></textarea>' +
'<h1> Email </h1>' +
'<textarea name="text" rows="1" cols="20"></textarea>' +
'<input type="submit" value="Submit text" />' +
'</body>' +
'</html>';
response.writeHead(200, { "Content-Type": "text/html" });
response.write(body);
response.end();
}
¿De dónde vino la respuesta nuevamente? postDatos? ¿Por qué no puedo definir una variable en esta "devolución de llamada" y luego usarla fuera de la devolución de llamada? ¿Hay alguna manera de que algunas cosas sean secuenciales y luego el resto del programa asíncrono?
No estoy seguro de dónde se utiliza esta función, pero el objetivo de las devoluciones de llamada es que las pases a alguna función que se ejecute de forma asincrónica; almacena su devolución de llamada, y cuando esa función finaliza con lo que necesita hacer, llamará su devolución de llamada con los parámetros necesarios. Probablemente lo mejor sea un ejemplo de adelante hacia atrás.
Imaginemos que tenemos un marco y en él hay una operación que se ejecuta durante mucho tiempo, recuperando algunos datos de la base de datos.
function getStuffFromDatabase() {
// this takes a long time
};
Como no queremos que se ejecute sincrónicamente, permitiremos que el usuario pase una devolución de llamada.
function getStuffFromDatabase(callback) {
// this takes a long time
};
Simularemos que tardamos mucho con una llamada a setTimeout
; También haremos como si obtuviéramos algunos datos de la base de datos, pero simplemente codificaremos un valor de cadena.
function getStuffFromDatabase(callback) {
setTimeout(function() {
var results = "database data";
}, 5000);
};
Finalmente, una vez que tengamos los datos, llamaremos a la devolución de llamada que nos proporcionó el usuario de la función del marco.
function getStuffFromDatabase(callback) {
setTimeout(function() {
var results = "database data";
callback(results);
}, 5000);
};
Como usuario del marco, harías algo como esto para usar la función:
getStuffFromDatabase(function(data) {
console.log("The database data is " + data);
});
Entonces, como puede ver data
(igual que response
en postData
su ejemplo), proviene de la función a la que pasa su devolución de llamada ; le proporciona esos datos cuando sabe cuáles deberían ser esos datos.
La razón por la que no puedes establecer un valor en tu devolución de llamada y usarlo fuera de la devolución de llamada es porque la devolución de llamada en sí no ocurre hasta más adelante.
// executed immediately executed sometime in the future
// | | by getStuffFromDatabase
// v v
getStuffFromDatabase(function(data) {
var results = data; // <- this isn't available until sometime in the future!
});
console.log(results); // <- executed immediately
Cuando se console.log
ejecuta, ¡la asignación de var results
aún no ha ocurrido!
Tienes varias preguntas no relacionadas aquí:
1) El poder de async es poder hacer varias cosas al mismo tiempo sin bloquear el hilo principal. En node y js en general, esto se aplica particularmente a las solicitudes de archivos ajax. Esto significa que puedo realizar varias llamadas asíncronas para recuperar archivos y no bloquear el hilo principal mientras procesa el contenido. Mi marco preferido es jQuery, que tiene un conveniente $.Deferred que encapsula y estandariza las llamadas asíncronas para el uso de jQuery.
2) respuesta y postData provienen del método principal. No hay nada mágico aquí, es una llamada de función normal, por lo que los valores de éstas se crean en otro lugar y se pasan a esta invocación. Dependiendo del marco de nodo que tenga, la firma exacta de su método cambiará.
3) Puede definir una variable global en su devolución de llamada si tiene el alcance adecuado. Sin embargo, parece que necesita ayuda para aprender qué es el alcance. Aquí hay algunos enlaces
http://www.digital-web.com/articles/scope_in_javascript/
http://robertnyman.com/2008/10/09/explaining-javascript-scope-and-closures/
4) Una vez que se vuelve asincrónico, nunca podrá volver atrás; sin embargo, al aprovechar la promesa y los objetos diferidos como con jQuery Deferreds, puede esperar a que se completen varios asincrónicos antes de continuar su ejecución en otro asincrónico. Los diferidos son tus amigos.
http://api.jquery.com/category/deferred-object/
Parece que estás trabajando en el Libro para principiantes de Node . Le animo a que lea todo el libro, realmente es una excelente introducción. Si está intentando comprender mejor Javascript, los vídeos de Douglas Crockford en YouTube son una excelente descripción general: 1 , 2 .
El fragmento de código que has publicado no tiene suficiente contexto para que pueda ayudarte realmente. response
es un parámetro que estás pasando a tu función, no proviene de postData. Si está trabajando con código de la manera que sugiere el Libro para principiantes de Node, probablemente esté pasando la respuesta a su función newuser desde la función createServer, que es parte del módulo http que viene con Node.
No puede definir una variable en la devolución de llamada y luego usarla en la devolución de llamada porque Javascript tiene un alcance léxico. Aquí hay una publicación de Stack Overflow sobre el tema del alcance de Javascript. El primer video de Doug Crockford que publiqué también tiene una excelente explicación de las reglas de alcance de Javascript.
Javascript no es necesariamente asíncrono. Simplemente proporciona funciones anónimas que son cierres, que son una herramienta útil para implementar fácilmente la lógica asíncrona. Nuevamente, el Libro para principiantes de Node muestra un buen ejemplo de cómo escribir código síncrono con Node (no lo que desea) y luego reescribirlo para hacerlo asíncrono.