JavaScript: solicitudes AJAX asincrónicas con ejemplos. JavaScript expresivo: HTTP

Una lección en la que veremos cómo crear solicitudes AJAX asincrónicas simples al servidor usando ejemplos. Usaremos los métodos GET y POST como método de transmisión de solicitudes. En el servidor, procesaremos las solicitudes utilizando scripts PHP.

¿Qué es una solicitud AJAX asincrónica?

La tecnología AJAX se utiliza principalmente para crear solicitudes asincrónicas al servidor. Una solicitud asincrónica es aquella que se ejecuta en segundo plano y no interfiere con la interacción del usuario con la página.

Al enviar una solicitud asincrónica, el navegador (página) no se "congela", es decir puedes trabajar con él como antes. Pero entonces ¿cómo saber cuándo llega la respuesta del servidor? Para determinar esto, debe monitorear la propiedad readyState del navegador. Esta propiedad contiene un número cuyo valor se puede utilizar para determinar en qué etapa se encuentra la solicitud. La siguiente tabla muestra los principales valores de la propiedad readyState y sus estados correspondientes.

Aquellos. Resulta que necesitamos rastrear cuándo el valor de la propiedad readyState es igual a 4. Esto significará que la solicitud enviada recibió una respuesta del servidor. Los valores restantes se utilizan con bastante poca frecuencia en la práctica y es posible que algunos navegadores no los admitan.

Para determinar en qué etapa se encuentra la solicitud, debe utilizar el evento onreadystatechange del objeto XMLHttpRequest. Este evento ocurre cada vez que cambia el valor de la propiedad readyState. Por lo tanto, en el controlador de este evento (función con nombre o sin nombre), puede escribir acciones que verificarán si esta propiedad es igual a 4 y, si es igual, luego, por ejemplo, mostrar la respuesta del servidor en la página.

Crear una solicitud AJAX asincrónica (método GET)

Veamos cómo crear una solicitud AJAX asincrónica usando un ejemplo, que saludará al usuario después de cargar la página y mostrará su dirección IP.

Para hacer esto, necesita crear 2 archivos en el servidor en un directorio:

  • bienvenido.html: página HTML que se mostrará al usuario. En la misma página colocaremos un script que realizará todas las acciones necesarias para que AJAX funcione del lado del cliente.
  • Processing.php: archivo PHP que procesará la solicitud en el lado del servidor y generará una respuesta. Comencemos el desarrollo creando la estructura básica del archivo bienvenido.html.
  • Un ejemplo de trabajo AJAX Un ejemplo de trabajo AJAX // Aquí colocaremos el código JavaScript que enviará una solicitud al servidor, la recibirá y actualizará el contenido de la página. Todo esto funcionará sin recargar la página.

    Veamos la secuencia de acciones que se deben realizar en el lado del cliente (en código JavaScript):

    Preparemos los datos necesarios para ejecutar la solicitud en el servidor. Si no se necesitan datos para completar la solicitud en el servidor, se puede omitir este paso.

    Creemos una variable que contendrá una instancia del objeto XHR (XMLHttpRequest).

    Configuremos la solicitud usando el método open().

    Se especifican los siguientes parámetros:

    • El método mediante el cual se enviará la solicitud al servidor (GET, POST).
    • La URL que procesará la solicitud en el servidor.
    • Tipo de solicitud: síncrona (falso) o asíncrona (verdadero).
    • Nombre de usuario y contraseña si es necesario.
  • Suscribámonos al evento onreadystatechange del objeto XHR y especifiquemos el controlador como una función anónima o con nombre. Después de eso, crearemos código dentro de esta función que verificará el estado de la respuesta y realizará ciertas acciones en la página. La respuesta que proviene del servidor siempre está en la propiedad ResponseText.

    Además de verificar el valor de la propiedad readyState número 4, también puede verificar el valor de la propiedad status. Esta propiedad determina el estado de la solicitud. Si es igual a 200, entonces todo está bien. De lo contrario, se produjo un error (por ejemplo, 404: URL no encontrada).

    Enviemos una solicitud al servidor usando el método send().

    Si utilizamos el método GET para enviar una solicitud, entonces no es necesario pasar datos al parámetro de este método. Se envían como parte de la URL.

    Si utilizamos el método POST para enviar una solicitud, entonces los datos deben pasarse como parámetro al método send(). Además, antes de llamar a este método, debe configurar el encabezado Content-Type para que el servidor sepa en qué codificación llegó la solicitud y pueda descifrarla.

    Contenido del elemento script:

    // 2. Creando la variable de solicitud var request = new XMLHttpRequest(); // 3. Configurar la solicitud request.open("GET","processing.php",true); // 4. Suscríbase al evento onreadystatechange y procéselo usando la función anónima request.addEventListener("readystatechange", function() ( // si los estados de la solicitud son 4 y el estado de la solicitud es 200 (OK) if ((request. readyState==4) && (request.status==200)) ( // por ejemplo, enviar el objeto XHR a la consola del navegador console.log(request); // y la respuesta (texto) que vino del servidor en la ventana de alerta console.log(request.responseText); // obtiene el elemento con id = bienvenido var bienvenido = document.getElementById("welcome"); // reemplaza el contenido del elemento con la respuesta que vino del servidor. .innerHTML = solicitud.responseText ) ); // 5. Enviar una solicitud al servidor request.send();

    Como resultado, el archivo bienvenido.html tendrá el siguiente código:

    Ejemplo de trabajo AJAX Ejemplo de trabajo AJAX window.addEventListener("load",function() ( var request = new XMLHttpRequest(); request.open("GET","processing.php",true); request.addEventListener(" readystatechange", función() ( if ((request.readyState==4) && (request.status==200)) ( var bienvenido = document.getElementById("bienvenido"); bienvenido.innerHTML = request.responseText; ) ) ); solicitud.enviar();

    En el servidor (usando php):

  • Consigamos los datos. Si los datos se envían mediante el método GET, entonces desde la matriz global $_GET["name"] . Y si los datos se transfieren mediante el método POST, entonces desde la matriz global $_POST["name"] .
  • Utilizando estos datos, realizaremos algunas acciones en el servidor. Como resultado, obtenemos alguna respuesta. Mostrémoslo usando echo .
  • Aquí todo es elemental, así que ni siquiera comentaré. Ahora viene la parte más difícil: el lado del cliente:


    /* Esta función crea un objeto XMLHTTP para varios navegadores */
    función getXmlHttp() (
    var xmlhttp;
    intentar (
    xmlhttp = nuevo ActiveXObject("Msxml2.XMLHTTP");
    ) atrapar (e) (
    intentar (
    xmlhttp = nuevo ActiveXObject("Microsoft.XMLHTTP");
    ) atrapar (E) (
    xmlhttp = falso;
    }
    }
    if (!xmlhttp && tipo de XMLHttpRequest!="indefinido") (
    xmlhttp = nuevo XMLHttpRequest();
    }
    devolver xmlhttp;
    }
    función suma() (
    var a = document.getElementById("a").value; // Leer el valor de a
    var b = document.getElementById("b").value; // Lee el valor de b
    var xmlhttp = getXmlHttp(); // Crea un objeto XMLHTTP
    xmlhttp.open("POST", "test.php", verdadero); // Abrir una conexión asincrónica
    xmlhttp.setRequestHeader("Tipo de contenido", "aplicación/x-www-form-urlencoded"); // Enviar codificación
    xmlhttp.send("a=" + encodeURIComponent(a) + "&b=" + encodeURIComponent(b)); // Enviar una solicitud POST
    xmlhttp.onreadystatechange = function() ( // Esperando una respuesta del servidor
    if (xmlhttp.readyState == 4) ( // Llegó la respuesta
    if(xmlhttp.status == 200) ( // El servidor devolvió el código 200 (lo cual es bueno)
    document.getElementById("summa").innerHTML = xmlhttp.responseText; // Imprime la respuesta del servidor
    }
    }
    };
    }









    La cantidad es:


    No comentaré el código HTML, ya que es completamente transparente. Pero agregaré un poco a JavaScript, a pesar de los comentarios detallados. Primero, la función getXmlHttp() es genérica. Puede copiarlo de forma segura en sus scripts. Su tarea es devolver dicho XMLHTTP para que funcione en cualquier navegador. Sin embargo, debido a que la opción más popular es new XMLHttpRequest(), no funciona en IE6, por ejemplo. Otras opciones tampoco son universales, por lo que aquí simplemente seleccionamos una opción que funcione para cualquier navegador.

    También escribí en los comentarios sobre "trabajo asincrónico". También existe una opción sincrónica. La única diferencia es que en modo síncrono, hasta que se reciba una respuesta del servidor, el navegador no funcionará, simplemente se colgará. Es difícil para mí encontrar una tarea que sea necesaria, así que inmediatamente escribí una versión asincrónica. Funciona así: enviamos una solicitud y esperamos una respuesta, pero el navegador no se bloquea. Y cuando llega la respuesta (xmlhttp.readyState == 4), inmediatamente procesamos la respuesta. Esta es la versión asincrónica del trabajo, es un poco más complicada, pero es la única que se debe utilizar (salvo casos muy raros).

    Así es como se envían las solicitudes POST a través de JavaScript. Como puedes ver, no necesitábamos Ajax en absoluto. Y le recomiendo encarecidamente que, si solo tiene un par de solicitudes para todo el sitio, ni siquiera piense en utilizar esta biblioteca de gran capacidad, sino que utilice el material de este artículo.

    El sueño por el que se creó la Red es un espacio de información común en el que nos comunicamos, compartimos información. Su versatilidad es una parte integral de esto: un enlace de hipertexto puede conducir a cualquier lugar, ya sea información personal, local o global, un borrador o un texto revisado.

    Tim Bernes-Lee, La World Wide Web: una historia personal muy breve

    Protocolo Si escribe eloquentjavascript.net/17_http.html en la barra de direcciones del navegador, el navegador primero reconocerá la dirección del servidor asociada con el nombre eloquentjavascript.net e intentará abrir una conexión TCP en el puerto 80, el puerto predeterminado para HTTP. Si el servidor existe y acepta la conexión, el navegador envía algo como:

    OBTENER /17_http.html HTTP/1.1
    Anfitrión: eloquentjavascript.net
    Agente de usuario: nombre del navegador

    El servidor responde en la misma conexión:

    HTTP/1.1 200 correcto
    Longitud del contenido: 65585
    Tipo de contenido: texto/html


    ...el resto del documento

    El navegador toma la parte que viene después de la respuesta después de la línea vacía y la muestra como un documento HTML.

    La información enviada por el cliente se llama solicitud. Comienza con la línea:

    OBTENER /17_http.html HTTP/1.1

    La primera palabra es el método de solicitud. GET significa que necesitamos obtener un recurso específico. Otros métodos comunes son DELETE para eliminar, PUT para reemplazar y POST para enviar información. Tenga en cuenta que no se requiere que el servidor cumpla con todas las solicitudes que recibe. Si elige un sitio aleatorio y le dice que BORRE la página de inicio, lo más probable es que se rechace.

    La parte después del nombre del método es la ruta al recurso al que se envió la solicitud. En el caso más simple, el recurso es simplemente un archivo en el servidor, pero el protocolo no se limita a esta posibilidad. Un recurso puede ser cualquier cosa que pueda transferirse como un archivo. Muchos servidores generan respuestas sobre la marcha. Por ejemplo, si abre twitter.com/marijnjh, el servidor buscará en la base de datos al usuario marijnjh y, si lo encuentra, creará una página de perfil para ese usuario.

    Siguiendo la ruta del recurso, la primera línea de la solicitud menciona HTTP/1.1 para indicar la versión del protocolo HTTP que está utilizando.

    La respuesta del servidor también comienza con la versión del protocolo, seguida del estado de la respuesta: primero un código de tres dígitos y luego una línea.

    HTTP/1.1 200 correcto

    Los códigos de estado que comienzan con 2 indican solicitudes exitosas. Los códigos que comienzan con 4 significan que algo salió mal. 404 es el estado HTTP más famoso, que indica que no se pudo encontrar el recurso solicitado. Los códigos que comienzan con 5 indican que hubo un error en el servidor, pero no culpa de la solicitud.

    La primera línea de una solicitud o respuesta puede ir seguida de cualquier número de líneas de encabezado. Son cadenas con el formato “nombre:valor” que indican información adicional sobre la solicitud o respuesta. Estos encabezados se incluyeron en el ejemplo:

    Longitud del contenido: 65585
    Tipo de contenido: texto/html
    Última modificación: miércoles, 09 de abril de 2014 10:48:09 GMT

    Aquí se determina el tamaño y tipo de documento recibido en respuesta. En este caso se trata de un documento HTML de 65.585 bytes. También indica cuándo se modificó el documento por última vez.

    En su mayor parte, el cliente o servidor determina qué encabezados deben incluirse en la solicitud o respuesta, aunque algunos encabezados son obligatorios. Por ejemplo, Host, que indica el nombre de host, debe incluirse en la solicitud porque un servidor puede servir muchos nombres de host en una sola dirección IP y, sin este encabezado, el servidor no sabrá con qué host está intentando comunicarse el cliente.

    Después de los encabezados, tanto la solicitud como la respuesta pueden especificar una línea vacía seguida de un cuerpo que contiene los datos a transmitir. Las solicitudes GET y DELETE no envían datos adicionales, pero PUT y POST sí. Algunas respuestas, como los mensajes de error, no requieren cuerpo.

    Navegador y HTTP Como vimos en el ejemplo, el navegador envía una solicitud cuando ingresamos una URL en la barra de direcciones. Cuando el documento HTML resultante contiene referencias a otros archivos, como imágenes o archivos JavaScript, también se solicitan al servidor.

    Un sitio web promedio puede contener fácilmente entre 10 y 200 recursos. Para poder solicitarlos más rápido, los navegadores realizan varias solicitudes a la vez en lugar de esperar a que finalicen una tras otra. Estos documentos siempre se solicitan mediante solicitudes GET.

    Las páginas HTML pueden tener formularios que permiten a los usuarios ingresar información y enviarla al servidor. Aquí hay un formulario de ejemplo:

    Nombre:

    Mensaje:

    Enviar

    El código describe un formulario con dos campos: el pequeño solicita un nombre y el grande solicita un mensaje. Cuando hace clic en el botón "Enviar", la información de estos campos se codificará en una cadena de consulta. Cuando el atributo del método de un elemento es GET, o cuando no se especifica en absoluto, la cadena de consulta se coloca en la URL del campo de acción y el navegador realiza una solicitud GET con esa URL.

    OBTENER /example/message.html?name=Jean&message=Yes%3F HTTP/1.1

    El comienzo de la cadena de consulta se indica con un signo de interrogación. A esto le siguen pares de nombres y valores correspondientes al atributo de nombre de los campos del formulario y el contenido de esos campos. El signo comercial (&) se utiliza para separarlos.

    El mensaje enviado en el ejemplo contiene la cadena “¿Sí?”, aunque el signo de interrogación se reemplaza por algún código extraño. Es necesario utilizar caracteres de escape en algunos caracteres de la cadena de consulta. Se incluye el signo de interrogación y está representado por el código %3F. Existe una regla no escrita que dice que cada formato debe tener una forma de escapar de los caracteres. Esta regla, llamada codificación de URL, utiliza un porcentaje seguido de dos dígitos hexadecimales que representan el código de carácter. 3F en decimal sería 63 y ese es el código del signo de interrogación. JavaScript tiene funciones encodeURIComponent y decodeURIComponent para codificar y decodificar.

    Console.log(encodeURIComponent("Hola y adiós")); // → Hola%20%26%20adiós console.log(decodeURIComponent("Hola%20%26%20adiós")); // → Hola y adios

    Si cambiamos el atributo del método en el formulario del ejemplo anterior a POST, la solicitud HTTP de envío del formulario se realizará utilizando el método POST, que enviará la cadena de consulta en el cuerpo de la solicitud en lugar de agregarla a la URL.

    POST /ejemplo/mensaje.html HTTP/1.1
    Longitud del contenido: 24
    Tipo de contenido: aplicación/x-www-form-urlencoded

    Nombre=Jean&mensaje=Sí%3F

    Por convención, el método GET se utiliza para solicitudes que no tienen efectos secundarios, como las búsquedas. Las solicitudes que cambian algo en el servidor (crear una nueva cuenta o publicar un mensaje) deben enviarse mediante el método POST. Los programas cliente, como los navegadores, saben que no es necesario simplemente realizar solicitudes POST y, a veces, realizar solicitudes GET sin que el usuario se dé cuenta, por ejemplo, para descargar por adelantado contenido que el usuario pronto necesitará.

    En el próximo capítulo, volveremos a los formularios y hablaremos sobre cómo podemos crearlos usando JavaScript.

    XMLHttpRequest La interfaz a través de la cual JavaScript en el navegador puede realizar solicitudes HTTP se llama XMLHttpRequest (observe cómo salta el tamaño de las letras). Fue desarrollado por Microsoft para el navegador Internet Explorer a finales de los años 1990. En aquella época, el formato XML era muy popular en el mundo del software empresarial, y en este mundo Microsoft siempre se ha sentido como en casa. Fue tan popular que se fijó la abreviatura XML delante del nombre de la interfaz para trabajar con HTTP, aunque esta última no tiene ninguna relación con XML.

    Sin embargo, el nombre no carece completamente de significado. La interfaz le permite analizar respuestas como si fueran documentos XML. Mezclar dos cosas diferentes (análisis de solicitudes y respuestas) en una es, por supuesto, un diseño desagradable, pero ¿qué se puede hacer?

    Cuando se agregó la interfaz XMLHttpRequest a Internet Explorer, fue posible hacer cosas que antes eran muy difíciles de hacer. Por ejemplo, los sitios comenzaron a mostrar listas de sugerencias mientras el usuario ingresa algo en un campo de texto. El script envía texto al servidor a través de HTTP al mismo tiempo que el usuario escribe. El servidor, que tiene una base de datos de posibles opciones de entrada, busca entre las entradas las adecuadas y las devuelve para su visualización. Se veía muy bien: antes la gente estaba acostumbrada a esperar a que se recargara toda la página después de cada interacción con el sitio.

    Otro navegador importante de la época, Mozilla (posteriormente Firefox), no quiso quedarse atrás. Para permitir que sucedan cosas similares, Mozilla copió la interfaz junto con el nombre. La próxima generación de navegadores hizo lo mismo y hoy XMLHttpRequest es el estándar de facto.

    Envío de una solicitud Para enviar una solicitud simple, creamos un objeto de solicitud con un constructor XMLHttpRequest y llamamos a los métodos open y send.

    Solicitud var = nueva XMLHttpRequest(); req.open("GET", "ejemplo/data.txt", falso); solicitud.enviar(nulo); console.log(req.responseText); // → Este es el contenido de data.txt

    El método abierto configura la solicitud. En nuestro caso, decidimos realizar una solicitud GET al archivo ejemplo/data.txt. Las URL que no comienzan con un nombre de protocolo (por ejemplo, http:) se denominan relativas, lo que significa que se interpretan en relación con el documento actual. Cuando comienzan con una barra diagonal (/), reemplazan la ruta actual, la parte después del nombre del servidor. De lo contrario, parte de la ruta actual hasta la última barra se coloca antes de la URL relativa.

    Después de abrir la solicitud, podemos enviarla utilizando el método de envío. El argumento es el cuerpo de la solicitud. Para solicitudes GET, se utiliza nulo. Si el tercer argumento para abrir fue falso, enviar regresará solo después de que se haya recibido una respuesta a nuestra solicitud. Para obtener el cuerpo de la respuesta, podemos leer la propiedad ResponseText del objeto de solicitud.

    Puede obtener otra información del objeto de respuesta. El código de estado está disponible en la propiedad de estado y el texto de estado está disponible en statusText. Los encabezados se pueden leer desde getResponseHeader.

    Solicitud var = nueva XMLHttpRequest(); req.open("GET", "ejemplo/data.txt", falso); solicitud.enviar(nulo); console.log(req.status, req.statusText); // → 200 OK console.log(req.getResponseHeader("tipo de contenido")); // → texto/sin formato

    Los nombres de los encabezados no distinguen entre mayúsculas y minúsculas. Por lo general, se escriben con una letra mayúscula al comienzo de cada palabra, por ejemplo, “Tipo de contenido”, pero “tipo de contenido” o “TIPO DE CONTENIDO” describirán el mismo título.

    El propio navegador agregará algunos encabezados, como "Host" y otros, que el servidor necesita para calcular el tamaño del cuerpo. Pero puedes agregar tus propios encabezados usando el método setRequestHeader. Esto es necesario para casos especiales y requiere la cooperación del servidor al que accede; es libre de ignorar los encabezados que no puede procesar.

    Solicitudes asincrónicas En el ejemplo, la solicitud finalizó cuando finalizó la llamada de envío. Esto es conveniente porque propiedades como texto de respuesta están disponibles de inmediato. Pero esto significa que nuestro programa esperará mientras el navegador y el servidor se comunican entre sí. Si hay una mala conexión, un servidor débil o un archivo grande, esto puede llevar mucho tiempo. Esto también es malo porque no se activará ningún controlador de eventos mientras el programa esté en modo de espera: el documento dejará de responder a las acciones del usuario.

    Si pasamos verdadero como tercer argumento para abrir, la solicitud será asincrónica. Esto significa que cuando se llama a enviar, la solicitud se pone en cola para su envío. El programa continúa ejecutándose y el navegador se encarga de enviar y recibir datos en segundo plano.

    Pero mientras se procesa la solicitud, no recibiremos respuesta. Necesitamos un mecanismo de notificación de que los datos han llegado y están listos. Para hacer esto, necesitaremos escuchar el evento "cargar".

    Solicitud var = nueva XMLHttpRequest(); req.open("GET", "ejemplo/data.txt", verdadero); req.addEventListener("cargar", función() ( console.log("Listo:", req.status); )); solicitud.enviar(nulo);

    Al igual que la llamada requestAnimationFrame en el Capítulo 15, este código nos obliga a usar un estilo de programación asincrónico, envolviendo el código que debe ejecutarse después de la solicitud en una función y organizando que la función se llame en el momento adecuado. Volveremos a esto más tarde.

    Recuperar datos XML

    Cuando el recurso devuelto por el objeto XMLHttpRequest es un documento XML, la propiedad ResponseXML contendrá una representación analizada del documento. Funciona de manera similar al DOM, excepto que no tiene funcionalidad HTML nativa como la propiedad de estilo. El objeto contenido en ResponseXML corresponde al objeto del documento. Su propiedad documentElement hace referencia a la etiqueta externa del documento XML. En el siguiente documento (ejemplo/fruit.xml) esta etiqueta sería:

    Podemos obtener un archivo como este:

    Solicitud var = nueva XMLHttpRequest(); req.open("GET", "ejemplo/fruta.xml", falso); solicitud.enviar(nulo); console.log(req.responseXML.querySelectorAll("fruta").length); // → 3

    Los documentos XML se pueden utilizar para intercambiar información estructurada con el servidor. Su forma, etiquetas anidadas, es buena para almacenar la mayoría de los datos, o al menos mejor que los archivos de texto. La interfaz DOM es complicada en términos de recuperación de información y los documentos XML tienden a ser bastante detallados. Por lo general, es mejor comunicarse utilizando datos JSON, que son más fáciles de leer y escribir tanto para los programas como para los humanos.

    Solicitud var = nueva XMLHttpRequest(); req.open("GET", "ejemplo/fruta.json", falso); solicitud.enviar(nulo); console.log(JSON.parse(req.responseText)); // → (plátano: "amarillo", limón: "amarillo", cereza: "rojo")

    Las solicitudes HTTP de HTTP Sandbox desde una página web plantean problemas de seguridad. La persona que controla el script puede tener intereses diferentes a los intereses del usuario en cuyo ordenador se ejecuta. Específicamente, si voy a themafia.org, no quiero que sus scripts puedan realizar solicitudes a mybank.com utilizando la información de mi navegador como identificador y ordenarles que envíen todo mi dinero a alguna cuenta de la mafia.

    Los sitios web pueden protegerse de este tipo de ataques, pero hacerlo requiere cierto esfuerzo y muchos sitios no lo hacen. Debido a esto, los navegadores los protegen impidiendo que los scripts realicen solicitudes a otros dominios (nombres como themafia.org y mybank.com).

    Esto puede interferir con el desarrollo de sistemas que necesitan acceder a diferentes dominios por una buena razón. Afortunadamente, el servidor puede incluir el siguiente encabezado en la respuesta, dejando claro a los navegadores que la solicitud puede provenir de otros dominios:

    Control-de-acceso-permitir-origen: *

    Resumen de solicitudes En el Capítulo 10, utilizamos la función hipotética backgroundReadFile en nuestra implementación del sistema modular AMD. Tomó un nombre de archivo y una función, y llamó a esa función después de leer el contenido del archivo. Aquí hay una implementación simple de esta función:

    Función backgroundReadFile(url, devolución de llamada) ( var req = new XMLHttpRequest(); req.open("GET", url, true); req.addEventListener("load", function() ( if (req.status< 400) callback(req.responseText); }); req.send(null); }

    La abstracción simple facilita el uso de XMLHttpRequest para solicitudes GET simples. Si está escribiendo un programa que realiza solicitudes HTTP, es una buena idea utilizar una función auxiliar para no tener que repetir el feo patrón XMLHttpRequest todo el tiempo.

    El argumento de devolución de llamada es un término que se utiliza a menudo para describir este tipo de funciones. La función de devolución de llamada se pasa a otro código para que pueda devolvernos la llamada más tarde.

    Es fácil escribir su propia función auxiliar HTTP, diseñada específicamente para su programa. El anterior solo realiza solicitudes GET y no nos da control sobre los encabezados o el cuerpo de la solicitud. Puede escribir otra opción para la solicitud POST, o una más general que admita diferentes solicitudes. Muchas bibliotecas de JavaScript proporcionan contenedores para XMLHttpRequest.

    El principal problema con el contenedor dado es el manejo de errores. Cuando una solicitud devuelve un código de estado que indica un error (400 o superior), no hace nada. En algunos casos esto está bien, pero imagina si ponemos un indicador de carga en la página para mostrar que estamos recibiendo información. Si la solicitud falla porque el servidor falló o la conexión se interrumpió, la página simulará estar ocupada con algo. El usuario esperará un poco, luego se aburrirá y decidirá que el sitio es algo estúpido.

    Necesitamos una opción en la que recibamos una advertencia sobre una solicitud fallida para que podamos tomar medidas. Por ejemplo, podemos eliminar el mensaje de carga y hacerle saber al usuario que algo salió mal.

    El manejo de errores en código asíncrono es incluso más difícil que en código síncrono. Dado que a menudo tenemos que separar una parte del trabajo y colocarla en una función de devolución de llamada, el alcance del bloque try deja de tener sentido. En el siguiente código, no se detectará la excepción porque la llamada a backgroundReadFile regresa inmediatamente. Luego, el control abandona el bloque try y no se llamará a la función que contiene.

    Pruebe ( backgroundReadFile("ejemplo/data.txt", function(text) ( if (text != "esperado") throw new Error("Eso fue inesperado"); )); ) catch (e) ( console.log( "Hola desde el bloque catch");

    Para manejar solicitudes fallidas, tendremos que pasar una función adicional a nuestro contenedor y llamarla en caso de problemas. Otra opción es utilizar la convención de que si la solicitud falla, se pasa un argumento adicional a la función de devolución de llamada que describe el problema. Ejemplo:

    Función getURL(url, devolución de llamada) ( var req = new XMLHttpRequest(); req.open("GET", url, true); req.addEventListener("load", function() ( if (req.status< 400) callback(req.responseText); else callback(null, new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(null, new Error("Network error")); }); req.send(null); }

    El código que usa getURL debe verificar si se devuelve un error y manejarlo si lo hay.

    GetURL("data/nonsense.txt", function(content, error) ( if (error!= null) console.log("Error al recuperar el archivo nonsense.txt: " + error); else console.log("nonsense.txt : " + contenido); ));

    Esto no ayuda con las excepciones. Cuando realizamos varias acciones asincrónicas seguidas, una excepción en cualquier punto de la cadena en cualquier caso (a menos que envuelva cada controlador en su propio bloque try/catch) se lanzará al nivel superior e interrumpirá toda la cadena.

    Promesas Es difícil escribir código asincrónico para proyectos complejos en forma de simples devoluciones de llamada. Es fácil olvidarse de comprobar si hay errores o permitir que una excepción inesperada finalice abruptamente su programa. Además, organizar el manejo adecuado de errores y transmitir un error a través de múltiples devoluciones de llamada sucesivas es muy tedioso.

    Ha habido muchos intentos de resolver este problema con abstracciones adicionales. Uno de los intentos más exitosos se llama promesas. Las promesas envuelven una acción asincrónica en un objeto que se puede transmitir y que debe hacer algunas cosas cuando la acción se completa o falla. Esta interfaz ya forma parte de la versión actual de JavaScript y, para versiones anteriores, se puede utilizar como biblioteca.

    La interfaz de promesas no es particularmente intuitiva, pero es poderosa. En este capítulo lo describiremos sólo parcialmente. Puede encontrar más información en www.promisejs.org

    Para crear un objeto de promesas, llamamos al constructor Promise y le asignamos una función para inicializar una acción asincrónica. El constructor llama a esta función y le pasa dos argumentos, que en sí mismos son funciones. El primero debe convocarse en un caso exitoso y el otro en uno fallido.

    Y aquí está nuestro contenedor de solicitud GET, que esta vez devuelve una promesa. Ahora simplemente lo llamaremos get.

    Función get(url) ( devuelve nueva Promesa(función(éxito, error) ( var req = new XMLHttpRequest(); req.open("GET", url, true); req.addEventListener("load", function() ( si (solicitud.estado< 400) succeed(req.responseText); else fail(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { fail(new Error("Network error")); }); req.send(null); }); }

    Tenga en cuenta que la interfaz de la función en sí se ha simplificado. Le pasamos una URL y devuelve una promesa. Actúa como un controlador para la salida de la solicitud. Tiene un método then que se llama con dos funciones: una para manejar el éxito y otra para manejar el fracaso.

    Get("ejemplo/datos.txt").luego(función(texto) ( console.log("data.txt: " + texto); ), función(error) ( console.log("Error al recuperar datos.txt : " + error); ));

    Por ahora, esta sigue siendo una forma de expresar lo que ya hemos hecho. Sólo cuando tienes una cadena de eventos se hace visible una diferencia notable.

    Luego, la llamada produce una nueva promesa cuyo resultado (el valor pasado a los controladores de éxito) depende del valor de retorno de la primera función que le pasamos en ese momento. Esta función puede devolver otra promesa, indicando que se está realizando trabajo asincrónico adicional. En este caso, la promesa devuelta por entonces esperará la promesa devuelta por la función del controlador, y se producirá éxito o fracaso con el mismo valor. Cuando una función de controlador devuelve un valor que no es una promesa, la promesa devuelta por entonces se vuelve exitosa y utiliza ese valor como resultado.

    Esto significa que puedes usar then para cambiar el resultado de una promesa. Por ejemplo, la siguiente función devuelve una promesa cuyo resultado es el contenido de la URL dada analizada como JSON:

    Función getJSON(url) ( return get(url).luego(JSON.parse); )

    La última llamada a then no especificó un controlador de fallas. Esto es aceptable. El error se pasará a la promesa devuelta a través de entonces, que es lo que necesitamos: getJSON no sabe qué hacer cuando algo sale mal, pero con suerte el código que lo llama sí.

    Como ejemplo que muestra el uso de promesas, escribiremos un programa que obtenga una cantidad de archivos JSON del servidor y muestre la palabra "descargar" cuando se ejecute la solicitud. Los archivos contienen información sobre personas y enlaces a otros archivos con información sobre otras personas en propiedades como padre, madre, cónyuge.

    Necesitamos obtener el nombre de la madre del cónyuge de example/bert.json. En caso de problemas, debemos eliminar el texto “cargando” y mostrar un mensaje de error. Así es como puedes hacerlo usando promesas:

    función showMessage(msg) ( var elt = document.createElement("div"); elt.textContent = msg; return document.body.appendChild(elt); ) var cargando = showMessage("Cargando...");

    El programa final es relativamente compacto y legible. El método catch es similar a entonces, pero solo espera un controlador para el resultado fallido y, si tiene éxito, transmite el resultado sin cambios. La ejecución del programa continuará de la forma habitual después de detectar la excepción, tal como en el caso de try/catch. Entonces, el último entonces, que elimina el mensaje de carga, se ejecuta de todos modos, incluso si falla.

    Puede pensar en la interfaz de promesa como un lenguaje separado para procesar de forma asincrónica la ejecución del programa. Las llamadas adicionales a los métodos y funciones que se necesitan para que funcione le dan al código una apariencia un tanto extraña, pero no tan incómoda como manejar todos los errores manualmente.

    Apreciar HTTP Al crear un sistema en el que un programa JavaScript en el navegador (cliente) se comunica con un programa de servidor, puede utilizar varias opciones para modelar dicha comunicación.

    Un método común son las llamadas a procedimientos remotos. En este modelo, la comunicación sigue el patrón de llamadas a funciones regulares, solo estas funciones se ejecutan en otra computadora. La llamada es para crear una solicitud al servidor que incluye el nombre de la función y los argumentos. La respuesta a la solicitud incluye un valor de retorno.

    Cuando se utilizan llamadas a procedimientos remotos, HTTP es solo un transporte para la comunicación y probablemente escribirá una capa de abstracción que lo oculte por completo.

    Otro enfoque es construir su sistema de comunicación en torno al concepto de recursos y métodos HTTP. En lugar de llamar a un procedimiento remoto llamado addUser, realiza una solicitud PUT a /users/larry. En lugar de codificar las propiedades del usuario en argumentos de función, define un formato de documento o utiliza un formato existente que representará al usuario. El cuerpo de una solicitud PUT que crea un nuevo recurso será simplemente un documento de este formato. Un recurso se obtiene mediante una solicitud GET a su URL (/usuario/larry), que devuelve un documento que representa ese recurso.

    El segundo enfoque facilita el uso de algunas de las funciones de HTTP, como la compatibilidad con el almacenamiento en caché de recursos (una copia del recurso se almacena en el lado del cliente). También ayuda a crear una interfaz coherente porque es más fácil pensar en términos de recursos que en términos de funciones.

    Seguridad y HTTPS Los datos viajan a través de Internet por un camino largo y peligroso. Para llegar a su destino, deben atravesar todo tipo de lugares, desde la red Wi-Fi de una cafetería hasta redes controladas por diferentes organizaciones y estados. En cualquier punto del camino se pueden leer o incluso cambiar.

    Si necesita mantener algo en secreto, como contraseñas de correo electrónico, o datos que deben llegar a su destino sin cambios, como el número de cuenta bancaria al que está transfiriendo dinero, el simple HTTP no es suficiente.

    HTTP seguro, cuyas URL comienzan con https://, envuelve el tráfico HTTP para que sea más difícil de leer y cambiar. Primero, el cliente verifica que el servidor es quien dice ser exigiéndole que presente un certificado criptográfico emitido por una parte autorizada que el navegador reconozca. Luego, todos los datos que pasan a través de la conexión se cifran para evitar escuchas y alteraciones.

    Entonces, cuando todo funciona correctamente, HTTPS previene tanto los casos en los que alguien se hace pasar por otro sitio web con el que te estás comunicando como los casos de escuchas ilegales en tu comunicación. No es perfecto y ha habido casos en los que HTTPS falló debido a certificados falsos o robados o software dañado. Sin embargo, es muy fácil hacer algo malo con HTTP, y hackear HTTPS requiere el tipo de esfuerzo que sólo las agencias gubernamentales u organizaciones criminales muy serias (y a veces no hay diferencia entre estas organizaciones) pueden realizar.

    Resumen En este capítulo, vimos que HTTP es un protocolo para acceder a recursos en Internet. El cliente envía una solicitud que contiene un método (normalmente GET) y una ruta que especifica el recurso. El servidor decide qué hacer con la solicitud y responde con un código de estado y un cuerpo de respuesta. Las solicitudes y respuestas pueden contener encabezados que transmiten información adicional.

    Los navegadores realizan solicitudes GET para obtener los recursos necesarios para mostrar una página. La página puede contener formularios que permiten que la información ingresada por el usuario se envíe en una solicitud que se crea después de enviar el formulario. Aprenderá más sobre esto en el próximo capítulo.

    La interfaz a través de la cual JavaScript realiza solicitudes HTTP desde el navegador se llama XMLHttpRequest. Puede ignorar el prefijo "XML" (pero aún así deberá escribirlo). Se puede utilizar de dos maneras: sincrónico, que bloquea todo el trabajo hasta que se completa la solicitud, y asíncrono, que requiere instalar un controlador de eventos que monitorea el final de la solicitud. En casi todos los casos, se prefiere el método asincrónico. Crear una solicitud se ve así:

    Solicitud var = nueva XMLHttpRequest(); req.open("GET", "ejemplo/data.txt", verdadero); req.addEventListener("cargar", función() ( console.log(req.statusCode); )); solicitud.enviar(nulo);

    La programación asincrónica no es algo fácil. Las promesas son una interfaz que lo hace más fácil al ayudar a enrutar mensajes de error y excepciones al controlador correcto y al abstraer algunos de los elementos duplicados propensos a errores.

    Ejercicios Negociación de contenido Una de las cosas que HTTP puede hacer y que no hemos discutido se llama negociación de contenido. El encabezado Aceptar en una solicitud se puede utilizar para indicarle al servidor qué tipos de documentos desea recibir el cliente. Muchos servidores lo ignoran, pero cuando el servidor conoce las diferentes formas de codificar un recurso, puede mirar el encabezado y enviar la que prefiera el cliente.

    La URL eloquentjavascript.net/author está configurada para responder con texto sin formato, HTML o JSON, según la solicitud del cliente. Estos formatos están definidos por los tipos de contenido estandarizados texto/plain, texto/html y aplicación/json.

    Envíe una solicitud para recibir los tres formatos de este recurso. Utilice el método setRequestHeader del objeto XMLHttpRequest para establecer el encabezado Aceptar en uno de los tipos de contenido deseados. Asegúrese de configurar el encabezado después de abrir pero antes de enviar.

    Finalmente, intente solicitar contenido como aplicación/arcoíris+unicornios y vea qué sucede.

    Esperando múltiples promesas El constructor Promise tiene un método all que, dada una matriz de promesas, devuelve una promesa que espera a que se completen todas las promesas de la matriz. Luego devuelve un resultado exitoso y devuelve una matriz con los resultados. Si alguna de las promesas de la matriz falla, la promesa general también devuelve un error (con el valor de la promesa fallida de la matriz).

    Pruebe algo similar escribiendo la función all.

    Tenga en cuenta que una vez que se completa una promesa (cuando tiene éxito o falla), no puede fallar ni volver a tener éxito, y se ignoran las llamadas adicionales a la función. Esto puede hacer que sea más fácil manejar los errores en su promesa.

    Función todas (promesas) ( return new Promise(function(success, fail) ( // Tu código. )); ) // Código de verificación. all().then(function(array) ( console.log("Esto debería ser :", array); )); función pronto(val) ( devolver nueva Promesa(función(éxito) ( setTimeout(función() ( éxito(val); ), Math.random() * 500); )); ) todo().luego(función(matriz ) ( console.log("Esto debería ser :", matriz); )); función fallar() ( devolver nueva Promesa(función(éxito, fallar) ( fallar(nuevo Error("bang")); )); ) all().luego(función(matriz) ( console.log("Aquí es donde obtenemos no debería "); ), function(error) ( if (error.message != "bang") console.log("Bummer inesperado:", error); ));



    
    Arriba