Capítulo 7: APIs y Interfaces Web
7.1 Fetch API para Solicitudes HTTP
Bienvenido al capítulo 7, "APIs y Interfaces Web". En este esclarecedor capítulo, profundizaremos en las versátiles interfaces y APIs que ofrecen los navegadores web actuales. Con estas APIs a nuestra disposición, nosotros, como desarrolladores web, tenemos el poder de crear aplicaciones web ricas e interactivas que aprovechan al máximo no solo las capacidades del navegador, sino también el potencial del sistema operativo subyacente.
Este capítulo promete cubrir una amplia gama de APIs web, desde aquellas principalmente involucradas en realizar solicitudes HTTP, hasta las que son expertas en manejar archivos, gestionar diversos medios e incluso aquellas que interactúan directamente con el hardware del dispositivo.
En la era digital en rápida evolución, las aplicaciones web frecuentemente necesitan establecer comunicación con servidores externos, obtener datos cruciales, enviar actualizaciones oportunas e interactuar de manera dinámica y fluida con los usuarios. Tener un sólido entendimiento y un uso efectivo de las APIs web se convierte en una habilidad crítica para construir aplicaciones responsivas. Para equiparte con esta habilidad esencial, comenzamos este capítulo con una exploración profunda del Fetch API, un enfoque contemporáneo y flexible para realizar solicitudes HTTP, diseñado para la web moderna.
El Fetch API representa una interfaz moderna y sofisticada que ofrece la capacidad de realizar solicitudes de red similares a lo que es posible con XMLHttpRequest (XHR). Sin embargo, en comparación con XMLHttpRequest, el Fetch API trae a la mesa un rango mucho más potente y flexible de características. Una de las mejoras clave del Fetch API es su uso de promesas.
Las promesas son un enfoque contemporáneo para gestionar operaciones asíncronas, que son operaciones que no tienen que completarse antes de que otro código pueda ejecutarse. Al usar promesas, el Fetch API permite que el código se escriba y lea de una manera mucho más limpia y organizada, mejorando así la eficiencia del proceso de codificación y llevando a un código más mantenible a largo plazo.
7.1.1 Uso Básico de Fetch API
La función fetch()
sirve como columna vertebral del Fetch API, una herramienta significativa en el desarrollo web moderno. Es una función increíblemente versátil que permite una amplia gama de solicitudes de red como GET, POST, PUT, DELETE, entre otros tipos de solicitud. Estas solicitudes son esenciales para interactuar con servidores y manipular datos en la web.
La solicitud GET, por ejemplo, se usa frecuentemente para recuperar datos de un servidor en formato JSON. Los datos recuperados pueden ser cualquier tipo de información que esté almacenada en el servidor. Aquí tienes una breve demostración de cómo puedes usar la función fetch()
para ejecutar una sencilla solicitud HTTP GET con el fin de obtener datos JSON de un servidor.
Ejemplo: Obtener Datos JSON
fetch('<https://api.example.com/data>')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('There was a problem with your fetch operation:', error));
Este fragmento de código demuestra el uso del Fetch API. El Fetch API utiliza promesas, un enfoque contemporáneo para gestionar operaciones asíncronas, que son operaciones que no tienen que terminar antes de que el resto del código pueda ejecutarse. Esto permite un código más eficiente, mantenible y legible.
En este ejemplo, estamos usando el Fetch API para hacer una solicitud HTTP GET a un servidor. La solicitud GET se usa a menudo para recuperar datos de un servidor, y en este caso, se espera que los datos estén en formato JSON. El servidor al que estamos solicitando datos está especificado por la URL 'https://api.example.com/data'.
La operación fetch comienza con la función fetch()
, que devuelve una promesa. Esta promesa se resuelve en el objeto Response
que representa la respuesta a la solicitud. El objeto Response
contiene información sobre la respuesta del servidor, incluida la estatus de la solicitud.
El primer método then()
en la cadena de promesas maneja la respuesta de la operación fetch. Dentro de este método, estamos verificando si la respuesta fue exitosa utilizando la propiedad ok
del objeto Response
. Esta propiedad devuelve un Booleano que indica si el estado de la respuesta está dentro del rango exitoso (200-299).
Si la respuesta no fue OK, lanzamos un Error con un mensaje personalizado que incluye el texto del estado de la respuesta. El texto del estado proporciona una explicación legible por humanos del estado de la respuesta, como 'Not Found' para un estado 404.
Si la respuesta fue OK, devolvemos el cuerpo de la respuesta analizado como JSON utilizando el método json()
. Este método lee el flujo de la respuesta hasta completarla y analiza el resultado como un objeto JSON.
El segundo método then()
en la cadena de promesas recibe los datos JSON analizados del then()
anterior. Aquí, simplemente estamos registrando los datos en la consola.
El método catch()
al final de la cadena de promesas se usa para capturar cualquier error que pueda ocurrir durante la operación fetch o durante el análisis de los datos JSON. Si se captura un error, lo registramos en la consola con un mensaje de error personalizado.
En resumen, este fragmento de código demuestra el uso básico del Fetch API para hacer una solicitud HTTP GET, verificar si la respuesta fue exitosa, analizar los datos de la respuesta como JSON y manejar cualquier error que pueda ocurrir durante la operación.
7.1.2 Realizar Solicitudes POST con Fetch
Cuando necesitas enviar datos a un servidor, un método eficiente que puedes emplear es realizar una solicitud POST utilizando el Fetch API. Este proceso implica la especificación clara del método de la solicitud como 'POST'. Además, los datos que deseas transmitir deben incluirse en el cuerpo de la solicitud.
Estos datos pueden ser de varios tipos, como JSON o datos de formulario, dependiendo de lo que el servidor esté configurado para recibir. El Fetch API hace que este proceso sea sencillo e intuitivo, simplificando la tarea de enviar datos a servidores y haciendo tus tareas de desarrollo web más eficientes.
Ejemplo: Realizar una Solicitud POST
fetch('<https://api.example.com/data>', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'John',
email: 'john@example.com'
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
Este ejemplo demuestra el uso del Fetch API para realizar una solicitud HTTP POST a una URL específica, en este caso, 'https://api.example.com/data'. El Fetch API es una API moderna basada en promesas para realizar solicitudes de red desde aplicaciones JavaScript, y ofrece más flexibilidad y características que el método más antiguo XMLHttpRequest (XHR).
El script comienza con la función fetch()
, a la cual le pasamos la URL deseada a la que queremos enviar nuestra solicitud. Inmediatamente después de la URL, se proporciona un objeto que configura los detalles específicos de nuestra solicitud. Este objeto de configuración incluye la propiedad method
configurada como 'POST', indicando que estamos enviando datos al servidor, no solo solicitando datos de él.
En la propiedad headers
del objeto de configuración, 'Content-Type' está configurado como 'application/json'. Esto le dice al servidor que estamos enviando datos en formato JSON.
La propiedad body
contiene los datos que estamos enviando al servidor, que deben ser convertidos en una cadena usando JSON.stringify()
porque HTTP es un protocolo basado en texto y requiere que cualquier dato enviado al servidor esté en formato de cadena. En este caso, un objeto que contiene las propiedades 'name' y 'email' se convierte en cadena y se incluye en el cuerpo de la solicitud.
Después de la función fetch()
, se construye una cadena de promesas para manejar la respuesta del servidor y cualquier error que pueda ocurrir durante la operación fetch. Esto se hace utilizando los métodos .then()
y .catch()
que son parte del Fetch API basado en promesas.
El primer bloque .then()
recibe la respuesta del servidor como su argumento. Dentro de este bloque, se realiza una verificación para ver si la respuesta fue exitosa utilizando la propiedad ok
del objeto response. Si la respuesta no fue correcta, se lanza un error con un mensaje personalizado y el texto del estado de la respuesta. Si la respuesta es correcta, se devuelve como JSON utilizando el método json()
del objeto response.
El segundo bloque .then()
recibe los datos JSON analizados del bloque anterior. Aquí es donde podemos interactuar con los datos devueltos por el servidor, en este caso, simplemente se registran en la consola con un mensaje de 'Éxito:'.
Finalmente, el bloque .catch()
al final de la cadena de promesas captura cualquier error que ocurra durante la operación fetch o durante el análisis de los datos JSON. Estos errores luego se registran en la consola con un mensaje de 'Error:'.
7.1.3 Manejo de Errores
El manejo efectivo de errores es crucial cuando se realizan solicitudes de red en cualquier proyecto de programación, ya que asegura la resistencia y confiabilidad de tu aplicación. En este sentido, el Fetch API es una herramienta poderosa para los desarrolladores.
El Fetch API proporciona una forma de capturar errores de red, como problemas de conectividad o errores del servidor, y manejar errores que puedan ocurrir durante el análisis de datos. Esto incluye problemas que pueden surgir al convertir los datos de respuesta en un formato utilizable. Utilizando el Fetch API, puedes implementar un mecanismo robusto de manejo de errores para tus solicitudes de red, mejorando así la experiencia del usuario.
Ejemplo: Manejo de Errores
fetch('<https://api.example.com/data>')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => {
console.error('There was a problem with your fetch operation:', error);
});
El código comienza con una llamada a la función fetch()
, pasando una URL en forma de cadena 'https://api.example.com/data'. Esto envía una solicitud GET a la URL especificada, que se espera devuelva algunos datos.
Esta función fetch()
devuelve una Promesa. Las Promesas en JavaScript representan la finalización o el fracaso de una operación asíncrona y su valor resultante. Se utilizan para manejar operaciones asíncronas como esta solicitud de red.
La Promesa devuelta se encadena con un método then()
. El método then()
toma una función de devolución de llamada que se ejecutará cuando la Promesa se resuelva con éxito. La función de devolución de llamada recibe la respuesta de la operación fetch como su argumento.
Dentro de la devolución de llamada, primero verificamos si la respuesta fue exitosa comprobando la propiedad ok
del objeto de respuesta. Si la respuesta no fue exitosa, se lanza un Error con un mensaje que dice 'La respuesta de la red no fue correcta', junto con el texto del estado de la respuesta.
Si la respuesta fue exitosa, la devolución de llamada devuelve otra Promesa llamando a response.json()
. Este método lee el flujo de la respuesta hasta completarlo y analiza el resultado como JSON.
El método then()
se encadena nuevamente para manejar el valor resuelto de la Promesa response.json()
. Esta devolución de llamada recibe los datos JSON analizados como su argumento y los registra en la consola.
Finalmente, un método catch()
se encadena al final de la cadena de Promesas. El método catch()
se utiliza para manejar cualquier rechazo de las Promesas en la cadena, incluidos los errores que puedan ocurrir durante la operación fetch o el análisis del JSON. Si se captura un error, el error se registra en la consola con un mensaje de error personalizado.
En resumen, este ejemplo de código demuestra cómo usar el Fetch API para realizar una solicitud de red, manejar la respuesta, analizar los datos de la respuesta como JSON y manejar cualquier error que pueda ocurrir durante estas operaciones.
7.1.4 Usar Async/Await con Fetch
El Fetch API, una herramienta poderosa y flexible para hacer solicitudes de red, puede usarse junto con async
y await
para crear un estilo más sincrónico de manejar operaciones asíncronas. Este enfoque nos permite escribir código que es más fácil de entender y depurar porque parece ser sincrónico, aunque en realidad se está ejecutando de forma asincrónica.
Esto es particularmente útil en escenarios donde necesitamos esperar la respuesta de una solicitud antes de hacer otra, por ejemplo, al encadenar solicitudes a APIs. Al usar async
y await
con el Fetch API, podemos simplificar en gran medida la estructura y la legibilidad de nuestro código.
El Fetch API facilita la obtención de recursos a través de la red y es una parte integral del desarrollo web moderno, permitiendo una forma más flexible y poderosa de realizar solicitudes HTTP en comparación con el XMLHttpRequest tradicional.
En JavaScript, async
y await
son palabras clave que proporcionan una forma de escribir código basado en promesas de manera más sincrónica. Permiten a los desarrolladores manejar operaciones asíncronas sin caer en el infierno de las devoluciones de llamada, mejorando la legibilidad y mantenibilidad del código.
Al usar async
y await
con el Fetch API, las operaciones asíncronas como las solicitudes de red o las operaciones de archivos pueden escribirse de una manera que parece ser bloqueante, pero en realidad no lo es. Esto significa que mientras la operación asíncrona se está procesando, el motor de JavaScript puede ejecutar otras operaciones sin ser bloqueado por la operación asíncrona pendiente.
Ejemplo: Usar Async/Await con Fetch
El uso de async
y await
con el Fetch API se ve algo así:
async function fetchData() {
try {
const response = await fetch('<https://api.example.com/data>');
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('There was a problem with your fetch operation:', error);
}
}
fetchData();
En este ejemplo, la función fetchData
se declara como async
, lo que indica que la función devolverá una promesa. Dentro de la función fetchData
, se utiliza la palabra clave await
antes del método fetch
y response.json()
. Esto le dice a JavaScript que pause la ejecución de la función fetchData
hasta que la promesa de fetch
y response.json()
se haya resuelto, y luego reanuda la ejecución y devuelve el valor resuelto.
Si ocurre un error durante la operación fetch o al analizar el JSON, se captura en el bloque catch
, evitando que el programa se bloquee y proporcionando una oportunidad para manejar el error de manera adecuada.
Combinar el Fetch API con async
y await
no solo mejora la legibilidad del código, sino que también facilita el manejo de errores y casos extremos, convirtiéndolo en una herramienta poderosa para desarrollar aplicaciones web complejas que dependen en gran medida de operaciones asíncronas.
7.1.5 Manejo de Tiempos de Espera con Fetch
El Fetch API no soporta nativamente tiempos de espera en las solicitudes. Sin embargo, se pueden implementar tiempos de espera utilizando la función Promise.race()
de JavaScript para mejorar la robustez de tus solicitudes de red, especialmente en entornos con condiciones de red poco confiables. Aunque el Fetch API proporciona una manera flexible y moderna de hacer solicitudes de red, no incluye soporte incorporado para tiempos de espera en las solicitudes.
Los tiempos de espera en las solicitudes son importantes para gestionar las solicitudes de red, especialmente en entornos donde las condiciones de red pueden ser poco fiables o inestables. Estos tiempos de espera ayudan a asegurar que tu aplicación se mantenga receptiva y no se quede colgada mientras espera que se complete una solicitud de red, proporcionando una mejor experiencia de usuario.
Para implementar tiempos de espera con el Fetch API, el documento sugiere usar la función Promise.race()
de JavaScript. Esta función toma un array de promesas y devuelve una promesa que se resuelve o rechaza tan pronto como una de las promesas en el array se resuelva o rechace, de ahí el nombre "race" (carrera).
Al usar Promise.race()
, puedes configurar una carrera entre la solicitud fetch y una promesa de tiempo de espera. Si la solicitud fetch se completa antes de que se cumpla el tiempo de espera, la promesa de la solicitud fetch se resolverá primero y se usará su resultado. Si el tiempo de espera ocurre antes de que se complete la solicitud fetch, la promesa de tiempo de espera se rechazará primero, permitiéndote manejar la situación de tiempo de espera según sea necesario.
Este enfoque mejora la robustez de tus solicitudes de red, dándote más control sobre su comportamiento y asegurando que tu aplicación pueda manejar una amplia gama de condiciones de red de manera efectiva. Esto es particularmente crucial en aplicaciones web modernas, donde una interacción fluida y receptiva con servidores externos y APIs es una parte clave para proporcionar una experiencia de usuario de alta calidad.
Ejemplo: Implementación de Tiempos de Espera en Fetch
function fetchWithTimeout(url, options, timeout = 5000) {
const fetchPromise = fetch(url, options);
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), timeout);
});
return Promise.race([fetchPromise, timeoutPromise]);
}
fetchWithTimeout('<https://api.example.com/data>')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Failed:', error));
Este fragmento de código de JavaScript introduce una función llamada fetchWithTimeout
, diseñada para enviar una solicitud de red a una URL específica con un tiempo de espera. Esta función es especialmente útil en escenarios donde se realizan solicitudes de red en entornos con conexiones potencialmente poco confiables o de alta latencia, y se desea evitar que la aplicación se bloquee indefinidamente esperando una respuesta que podría no llegar nunca.
Los parámetros de la función son url
, options
y timeout
. El url
es el endpoint al que se envía la solicitud, options
son los parámetros adicionales o encabezados que se desean incluir en la solicitud, y timeout
es el número máximo de milisegundos que se desea esperar por la respuesta antes de desistir. El tiempo de espera predeterminado es de 5000 milisegundos (o 5 segundos), pero se puede personalizar este valor según las necesidades.
La función funciona utilizando la API fetch
para realizar la solicitud, que es un método moderno y basado en promesas para hacer solicitudes de red en JavaScript. fetch
proporciona un enfoque más potente y flexible para realizar solicitudes HTTP en comparación con métodos más antiguos como XMLHttpRequest
.
Sin embargo, una limitación de la API fetch
es que no soporta nativamente tiempos de espera en las solicitudes. Para superar esta limitación, la función usa Promise.race
para establecer el tiempo de espera. Promise.race
es un método que toma un array de promesas y devuelve una nueva promesa que se resuelve tan pronto como una de las promesas de entrada se resuelva. En otras palabras, "compite" las promesas entre sí y proporciona el resultado de la que se resuelva más rápido.
En este caso, se está haciendo competir la solicitud fetch (que es una promesa que se resuelve cuando la solicitud se completa) contra una promesa de tiempo de espera (que es una promesa que se rechaza automáticamente después del período de tiempo especificado). Si la solicitud fetch se completa antes del tiempo de espera, la promesa fetch se resolverá primero y se usará su resultado. Si el tiempo de espera ocurre antes de que se complete la solicitud fetch, la promesa de tiempo de espera se rechazará primero, y se lanzará un Error
con el mensaje "Request timed out".
El uso de la función se demuestra en la parte final del fragmento de código. Aquí, se envía una solicitud GET
a 'https://api.example.com/data', se intenta analizar la respuesta como JSON usando el método .json()
, y luego se registran los datos resultantes en la consola si la solicitud es exitosa o se registra un mensaje de error si falla.
En este ejemplo, fetchWithTimeout
compite la promesa fetch contra una promesa de tiempo de espera, que se rechazará después de un período de tiempo especificado. Esto asegura que la aplicación pueda manejar situaciones en las que una solicitud podría colgarse más tiempo del esperado.
7.1.6 Respuestas en Streaming
El Fetch API soporta el streaming de respuestas, lo que permite empezar a procesar datos tan pronto como comienzan a llegar. Esto es particularmente útil para manejar grandes conjuntos de datos o medios en streaming.
El Fetch API es una característica poderosa que permite a los desarrolladores empezar a procesar datos tan pronto como comienzan a llegar, en lugar de tener que esperar a que el conjunto de datos completo se descargue. Esto es especialmente beneficioso cuando se manejan grandes conjuntos de datos o medios en streaming.
En los escenarios tradicionales de transferencia de datos, generalmente se tendría que esperar a que el conjunto de datos completo se descargue antes de poder empezar a procesarlo. Esto podría resultar en demoras significativas, especialmente al manejar grandes cantidades de datos o en escenarios donde la conectividad de red es deficiente.
Sin embargo, con la característica de Respuestas en Streaming del Fetch API, los datos se pueden procesar en fragmentos a medida que llegan. Esto significa que se puede comenzar a trabajar con los datos casi de inmediato, mejorando el rendimiento percibido de la aplicación y proporcionando una mejor experiencia de usuario.
Esta característica puede ser particularmente beneficiosa al desarrollar aplicaciones que necesitan manejar tareas como transmisión de video en vivo o procesamiento de datos en tiempo real, donde esperar a que se descargue el conjunto completo de datos no es práctico ni eficiente.
El Fetch API abstrae muchas de las complejidades asociadas con el streaming de datos, permitiendo a los desarrolladores centrarse en construir sus aplicaciones sin tener que preocuparse por los detalles subyacentes de la transmisión y el procesamiento de datos. Con su soporte para Respuestas en Streaming, el Fetch API es una herramienta invaluable para el desarrollo web moderno.
Ejemplo: Transmisión de una Respuesta con Fetch
async function fetchAndProcessStream(url) {
const response = await fetch(url);
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log('Received chunk', value);
// Process each chunk
}
console.log('Response fully processed');
}
fetchAndProcessStream('<https://api.example.com/large-data>');
Aquí hay un desglose paso a paso de lo que hace la función:
async function fetchAndProcessStream(url) {
: Esta línea declara una función asincrónica llamada 'fetchAndProcessStream'. La palabra clave 'async' indica que esta función devolverá una Promesa. La función toma un solo argumento 'url', que es la URL del recurso de datos que se desea obtener.const response = await fetch(url);
: Esta línea envía una solicitud fetch a la URL especificada y espera la respuesta. La palabra clave 'await' se utiliza para pausar la ejecución de la función hasta que la Promesa devuelta por el método fetch() se resuelva.const reader = response.body.getReader();
: Esta línea obtiene un flujo legible del cuerpo de la respuesta. Un flujo legible es un objeto que permite leer datos de una fuente de manera asincrónica y en streaming.while (true) {
: Esta línea inicia un bucle infinito. Este bucle continuará hasta que se rompa explícitamente.const { done, value } = await reader.read();
: Esta línea lee un fragmento de datos del flujo. El método read() devuelve una Promesa que se resuelve en un objeto. El objeto contiene dos propiedades: 'done' y 'value'. 'done' es un booleano que indica si el lector ha terminado de leer los datos, y 'value' es el fragmento de datos.if (done) break;
: Esta línea verifica si el lector ha terminado de leer los datos. Si 'done' es verdadero, el bucle se rompe y la función deja de leer datos del flujo.console.log('Received chunk', value);
: Esta línea registra cada fragmento recibido en la consola. Aquí es donde se podría agregar código para procesar cada fragmento de datos a medida que llega.console.log('Response fully processed');
: Después de que todos los fragmentos de datos se hayan recibido y procesado, esta línea registra 'Response fully processed' en la consola, indicando que toda la respuesta ha sido manejada.fetchAndProcessStream('<https://api.example.com/large-data>');
: La última línea del código es una llamada a la función fetchAndProcessStream, con la URL de un recurso de datos grande como argumento.
Esta función es particularmente útil cuando se manejan grandes conjuntos de datos o datos en streaming, ya que permite el procesamiento eficiente y en tiempo real de los datos a medida que llegan. En lugar de esperar a que se descargue todo el conjunto de datos antes de comenzar a procesarlo, esta función permite que la aplicación comience a trabajar con los datos casi de inmediato, mejorando el rendimiento percibido de la aplicación y proporcionando una mejor experiencia de usuario.
Este ejemplo de código demuestra cómo leer de una respuesta en streaming de manera incremental, lo cual puede mejorar el rendimiento percibido de tu aplicación web al manejar grandes cantidades de datos.
7.1.7 Fetch con CORS
Cross-Origin Resource Sharing (CORS) es un requisito común para aplicaciones web que realizan solicitudes a dominios diferentes del dominio de origen. Comprender cómo manejar CORS con Fetch es esencial para el desarrollo web moderno.
CORS es un mecanismo que permite o deniega a las aplicaciones web hacer solicitudes a un dominio que es diferente de su propio dominio de origen. Esto es un requisito común en el desarrollo web actual, ya que muchas aplicaciones web necesitan acceder a recursos, como fuentes, JavaScript y APIs, que están alojados en un dominio diferente.
La API Fetch es una API moderna y basada en promesas integrada en JavaScript que proporciona una manera flexible y poderosa de hacer solicitudes de red. Es una mejora con respecto al antiguo XMLHttpRequest y permite a los desarrolladores hacer solicitudes tanto a destinos de mismo origen como de origen cruzado, por lo que es una herramienta valiosa para manejar CORS.
Combinar Fetch con CORS permite a los desarrolladores hacer solicitudes de origen cruzado directamente desde sus aplicaciones web, proporcionando una forma de interactuar con otros sitios y servicios a través de sus APIs. Esto puede expandir enormemente las capacidades de una aplicación web, permitiéndole obtener datos de diversas fuentes, integrarse con otros servicios e interactuar con la web en general.
Sin embargo, al igual que con todo lo relacionado con la seguridad y la web, es importante usar estas herramientas con prudencia. CORS es una característica de seguridad diseñada para proteger a los usuarios y sus datos, por lo que es esencial entender cómo funciona y cómo usarlo correctamente. Fetch, aunque potente y flexible, es una API de bajo nivel que requiere una buena comprensión de HTTP y la política de mismo origen para ser utilizada de manera efectiva y segura.
La API Fetch y CORS son herramientas esenciales en el desarrollo web moderno. Comprender cómo funcionan juntos es clave para construir aplicaciones web sofisticadas que puedan interactuar con la web en general mientras protegen la seguridad del usuario.
Ejemplo: Fetch con CORS
fetch('<https://api.another-domain.com/data>', {
method: 'GET',
mode: 'cors', // Ensure CORS mode is set if needed
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('CORS or network error:', error));
En este ejemplo, estamos utilizando la API Fetch, una interfaz integrada en el navegador para realizar solicitudes HTTP. Está basada en promesas, lo que significa que devuelve una Promesa que se resuelve en el objeto Response que representa la respuesta a la solicitud.
Así es como funciona:
fetch('<https://api.another-domain.com/data>', {...})
: La funciónfetch()
se llama con la URL de la API a la que queremos acceder. Toma dos argumentos: la entrada y la configuración opcional (init). La entrada es la URL que estamos solicitando y la configuración es un objeto de opciones que contiene cualquier configuración personalizada que desees aplicar a la solicitud.method: 'GET'
: Esta opción indica el método de solicitud, en este caso, GET. El método GET se usa para solicitar datos de un recurso especificado.mode: 'cors'
: Este es el modo de la solicitud. Aquí se establece como 'cors', que significa Cross-Origin Resource Sharing (Intercambio de Recursos de Origen Cruzado). Este es un mecanismo que permite o bloquea los recursos solicitados en función del dominio de origen. Se necesita cuando queremos permitir solicitudes provenientes de diferentes dominios.headers: {...}
: Los encabezados de la solicitud se establecen en esta sección. El encabezado 'Content-Type' se establece en 'application/json', lo que significa que el servidor interpretará los datos enviados como un objeto JSON..then(response => response.json())
: Una vez realizada la solicitud, la API Fetch devuelve una Promesa que se resuelve en el objeto Response. El método.then()
es un método de Promesa utilizado para funciones de callback para el éxito y el fallo de las Promesas. Aquí, el objeto response se pasa a una función callback donde se convierte en formato JSON utilizando el métodojson()
..then(data => console.log(data))
: Después de la conversión a JSON, los datos se pasan a otro callback de.then()
donde se registran en la consola..catch(error => console.error('CORS or network error:', error))
: El métodocatch()
aquí se usa para capturar cualquier error que pueda ocurrir durante la operación fetch. Si ocurre un error durante la operación, se pasa a una función callback y se registra en la consola.
En resumen, este código envía una solicitud GET a la URL especificada y registra la respuesta (o cualquier error que pueda ocurrir) en la consola. El uso de promesas con los métodos .then()
y .catch()
permite manejar operaciones asíncronas, haciendo posible esperar la respuesta del servidor y manejarla una vez que esté disponible.
7.1 Fetch API para Solicitudes HTTP
Bienvenido al capítulo 7, "APIs y Interfaces Web". En este esclarecedor capítulo, profundizaremos en las versátiles interfaces y APIs que ofrecen los navegadores web actuales. Con estas APIs a nuestra disposición, nosotros, como desarrolladores web, tenemos el poder de crear aplicaciones web ricas e interactivas que aprovechan al máximo no solo las capacidades del navegador, sino también el potencial del sistema operativo subyacente.
Este capítulo promete cubrir una amplia gama de APIs web, desde aquellas principalmente involucradas en realizar solicitudes HTTP, hasta las que son expertas en manejar archivos, gestionar diversos medios e incluso aquellas que interactúan directamente con el hardware del dispositivo.
En la era digital en rápida evolución, las aplicaciones web frecuentemente necesitan establecer comunicación con servidores externos, obtener datos cruciales, enviar actualizaciones oportunas e interactuar de manera dinámica y fluida con los usuarios. Tener un sólido entendimiento y un uso efectivo de las APIs web se convierte en una habilidad crítica para construir aplicaciones responsivas. Para equiparte con esta habilidad esencial, comenzamos este capítulo con una exploración profunda del Fetch API, un enfoque contemporáneo y flexible para realizar solicitudes HTTP, diseñado para la web moderna.
El Fetch API representa una interfaz moderna y sofisticada que ofrece la capacidad de realizar solicitudes de red similares a lo que es posible con XMLHttpRequest (XHR). Sin embargo, en comparación con XMLHttpRequest, el Fetch API trae a la mesa un rango mucho más potente y flexible de características. Una de las mejoras clave del Fetch API es su uso de promesas.
Las promesas son un enfoque contemporáneo para gestionar operaciones asíncronas, que son operaciones que no tienen que completarse antes de que otro código pueda ejecutarse. Al usar promesas, el Fetch API permite que el código se escriba y lea de una manera mucho más limpia y organizada, mejorando así la eficiencia del proceso de codificación y llevando a un código más mantenible a largo plazo.
7.1.1 Uso Básico de Fetch API
La función fetch()
sirve como columna vertebral del Fetch API, una herramienta significativa en el desarrollo web moderno. Es una función increíblemente versátil que permite una amplia gama de solicitudes de red como GET, POST, PUT, DELETE, entre otros tipos de solicitud. Estas solicitudes son esenciales para interactuar con servidores y manipular datos en la web.
La solicitud GET, por ejemplo, se usa frecuentemente para recuperar datos de un servidor en formato JSON. Los datos recuperados pueden ser cualquier tipo de información que esté almacenada en el servidor. Aquí tienes una breve demostración de cómo puedes usar la función fetch()
para ejecutar una sencilla solicitud HTTP GET con el fin de obtener datos JSON de un servidor.
Ejemplo: Obtener Datos JSON
fetch('<https://api.example.com/data>')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('There was a problem with your fetch operation:', error));
Este fragmento de código demuestra el uso del Fetch API. El Fetch API utiliza promesas, un enfoque contemporáneo para gestionar operaciones asíncronas, que son operaciones que no tienen que terminar antes de que el resto del código pueda ejecutarse. Esto permite un código más eficiente, mantenible y legible.
En este ejemplo, estamos usando el Fetch API para hacer una solicitud HTTP GET a un servidor. La solicitud GET se usa a menudo para recuperar datos de un servidor, y en este caso, se espera que los datos estén en formato JSON. El servidor al que estamos solicitando datos está especificado por la URL 'https://api.example.com/data'.
La operación fetch comienza con la función fetch()
, que devuelve una promesa. Esta promesa se resuelve en el objeto Response
que representa la respuesta a la solicitud. El objeto Response
contiene información sobre la respuesta del servidor, incluida la estatus de la solicitud.
El primer método then()
en la cadena de promesas maneja la respuesta de la operación fetch. Dentro de este método, estamos verificando si la respuesta fue exitosa utilizando la propiedad ok
del objeto Response
. Esta propiedad devuelve un Booleano que indica si el estado de la respuesta está dentro del rango exitoso (200-299).
Si la respuesta no fue OK, lanzamos un Error con un mensaje personalizado que incluye el texto del estado de la respuesta. El texto del estado proporciona una explicación legible por humanos del estado de la respuesta, como 'Not Found' para un estado 404.
Si la respuesta fue OK, devolvemos el cuerpo de la respuesta analizado como JSON utilizando el método json()
. Este método lee el flujo de la respuesta hasta completarla y analiza el resultado como un objeto JSON.
El segundo método then()
en la cadena de promesas recibe los datos JSON analizados del then()
anterior. Aquí, simplemente estamos registrando los datos en la consola.
El método catch()
al final de la cadena de promesas se usa para capturar cualquier error que pueda ocurrir durante la operación fetch o durante el análisis de los datos JSON. Si se captura un error, lo registramos en la consola con un mensaje de error personalizado.
En resumen, este fragmento de código demuestra el uso básico del Fetch API para hacer una solicitud HTTP GET, verificar si la respuesta fue exitosa, analizar los datos de la respuesta como JSON y manejar cualquier error que pueda ocurrir durante la operación.
7.1.2 Realizar Solicitudes POST con Fetch
Cuando necesitas enviar datos a un servidor, un método eficiente que puedes emplear es realizar una solicitud POST utilizando el Fetch API. Este proceso implica la especificación clara del método de la solicitud como 'POST'. Además, los datos que deseas transmitir deben incluirse en el cuerpo de la solicitud.
Estos datos pueden ser de varios tipos, como JSON o datos de formulario, dependiendo de lo que el servidor esté configurado para recibir. El Fetch API hace que este proceso sea sencillo e intuitivo, simplificando la tarea de enviar datos a servidores y haciendo tus tareas de desarrollo web más eficientes.
Ejemplo: Realizar una Solicitud POST
fetch('<https://api.example.com/data>', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'John',
email: 'john@example.com'
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
Este ejemplo demuestra el uso del Fetch API para realizar una solicitud HTTP POST a una URL específica, en este caso, 'https://api.example.com/data'. El Fetch API es una API moderna basada en promesas para realizar solicitudes de red desde aplicaciones JavaScript, y ofrece más flexibilidad y características que el método más antiguo XMLHttpRequest (XHR).
El script comienza con la función fetch()
, a la cual le pasamos la URL deseada a la que queremos enviar nuestra solicitud. Inmediatamente después de la URL, se proporciona un objeto que configura los detalles específicos de nuestra solicitud. Este objeto de configuración incluye la propiedad method
configurada como 'POST', indicando que estamos enviando datos al servidor, no solo solicitando datos de él.
En la propiedad headers
del objeto de configuración, 'Content-Type' está configurado como 'application/json'. Esto le dice al servidor que estamos enviando datos en formato JSON.
La propiedad body
contiene los datos que estamos enviando al servidor, que deben ser convertidos en una cadena usando JSON.stringify()
porque HTTP es un protocolo basado en texto y requiere que cualquier dato enviado al servidor esté en formato de cadena. En este caso, un objeto que contiene las propiedades 'name' y 'email' se convierte en cadena y se incluye en el cuerpo de la solicitud.
Después de la función fetch()
, se construye una cadena de promesas para manejar la respuesta del servidor y cualquier error que pueda ocurrir durante la operación fetch. Esto se hace utilizando los métodos .then()
y .catch()
que son parte del Fetch API basado en promesas.
El primer bloque .then()
recibe la respuesta del servidor como su argumento. Dentro de este bloque, se realiza una verificación para ver si la respuesta fue exitosa utilizando la propiedad ok
del objeto response. Si la respuesta no fue correcta, se lanza un error con un mensaje personalizado y el texto del estado de la respuesta. Si la respuesta es correcta, se devuelve como JSON utilizando el método json()
del objeto response.
El segundo bloque .then()
recibe los datos JSON analizados del bloque anterior. Aquí es donde podemos interactuar con los datos devueltos por el servidor, en este caso, simplemente se registran en la consola con un mensaje de 'Éxito:'.
Finalmente, el bloque .catch()
al final de la cadena de promesas captura cualquier error que ocurra durante la operación fetch o durante el análisis de los datos JSON. Estos errores luego se registran en la consola con un mensaje de 'Error:'.
7.1.3 Manejo de Errores
El manejo efectivo de errores es crucial cuando se realizan solicitudes de red en cualquier proyecto de programación, ya que asegura la resistencia y confiabilidad de tu aplicación. En este sentido, el Fetch API es una herramienta poderosa para los desarrolladores.
El Fetch API proporciona una forma de capturar errores de red, como problemas de conectividad o errores del servidor, y manejar errores que puedan ocurrir durante el análisis de datos. Esto incluye problemas que pueden surgir al convertir los datos de respuesta en un formato utilizable. Utilizando el Fetch API, puedes implementar un mecanismo robusto de manejo de errores para tus solicitudes de red, mejorando así la experiencia del usuario.
Ejemplo: Manejo de Errores
fetch('<https://api.example.com/data>')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => {
console.error('There was a problem with your fetch operation:', error);
});
El código comienza con una llamada a la función fetch()
, pasando una URL en forma de cadena 'https://api.example.com/data'. Esto envía una solicitud GET a la URL especificada, que se espera devuelva algunos datos.
Esta función fetch()
devuelve una Promesa. Las Promesas en JavaScript representan la finalización o el fracaso de una operación asíncrona y su valor resultante. Se utilizan para manejar operaciones asíncronas como esta solicitud de red.
La Promesa devuelta se encadena con un método then()
. El método then()
toma una función de devolución de llamada que se ejecutará cuando la Promesa se resuelva con éxito. La función de devolución de llamada recibe la respuesta de la operación fetch como su argumento.
Dentro de la devolución de llamada, primero verificamos si la respuesta fue exitosa comprobando la propiedad ok
del objeto de respuesta. Si la respuesta no fue exitosa, se lanza un Error con un mensaje que dice 'La respuesta de la red no fue correcta', junto con el texto del estado de la respuesta.
Si la respuesta fue exitosa, la devolución de llamada devuelve otra Promesa llamando a response.json()
. Este método lee el flujo de la respuesta hasta completarlo y analiza el resultado como JSON.
El método then()
se encadena nuevamente para manejar el valor resuelto de la Promesa response.json()
. Esta devolución de llamada recibe los datos JSON analizados como su argumento y los registra en la consola.
Finalmente, un método catch()
se encadena al final de la cadena de Promesas. El método catch()
se utiliza para manejar cualquier rechazo de las Promesas en la cadena, incluidos los errores que puedan ocurrir durante la operación fetch o el análisis del JSON. Si se captura un error, el error se registra en la consola con un mensaje de error personalizado.
En resumen, este ejemplo de código demuestra cómo usar el Fetch API para realizar una solicitud de red, manejar la respuesta, analizar los datos de la respuesta como JSON y manejar cualquier error que pueda ocurrir durante estas operaciones.
7.1.4 Usar Async/Await con Fetch
El Fetch API, una herramienta poderosa y flexible para hacer solicitudes de red, puede usarse junto con async
y await
para crear un estilo más sincrónico de manejar operaciones asíncronas. Este enfoque nos permite escribir código que es más fácil de entender y depurar porque parece ser sincrónico, aunque en realidad se está ejecutando de forma asincrónica.
Esto es particularmente útil en escenarios donde necesitamos esperar la respuesta de una solicitud antes de hacer otra, por ejemplo, al encadenar solicitudes a APIs. Al usar async
y await
con el Fetch API, podemos simplificar en gran medida la estructura y la legibilidad de nuestro código.
El Fetch API facilita la obtención de recursos a través de la red y es una parte integral del desarrollo web moderno, permitiendo una forma más flexible y poderosa de realizar solicitudes HTTP en comparación con el XMLHttpRequest tradicional.
En JavaScript, async
y await
son palabras clave que proporcionan una forma de escribir código basado en promesas de manera más sincrónica. Permiten a los desarrolladores manejar operaciones asíncronas sin caer en el infierno de las devoluciones de llamada, mejorando la legibilidad y mantenibilidad del código.
Al usar async
y await
con el Fetch API, las operaciones asíncronas como las solicitudes de red o las operaciones de archivos pueden escribirse de una manera que parece ser bloqueante, pero en realidad no lo es. Esto significa que mientras la operación asíncrona se está procesando, el motor de JavaScript puede ejecutar otras operaciones sin ser bloqueado por la operación asíncrona pendiente.
Ejemplo: Usar Async/Await con Fetch
El uso de async
y await
con el Fetch API se ve algo así:
async function fetchData() {
try {
const response = await fetch('<https://api.example.com/data>');
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('There was a problem with your fetch operation:', error);
}
}
fetchData();
En este ejemplo, la función fetchData
se declara como async
, lo que indica que la función devolverá una promesa. Dentro de la función fetchData
, se utiliza la palabra clave await
antes del método fetch
y response.json()
. Esto le dice a JavaScript que pause la ejecución de la función fetchData
hasta que la promesa de fetch
y response.json()
se haya resuelto, y luego reanuda la ejecución y devuelve el valor resuelto.
Si ocurre un error durante la operación fetch o al analizar el JSON, se captura en el bloque catch
, evitando que el programa se bloquee y proporcionando una oportunidad para manejar el error de manera adecuada.
Combinar el Fetch API con async
y await
no solo mejora la legibilidad del código, sino que también facilita el manejo de errores y casos extremos, convirtiéndolo en una herramienta poderosa para desarrollar aplicaciones web complejas que dependen en gran medida de operaciones asíncronas.
7.1.5 Manejo de Tiempos de Espera con Fetch
El Fetch API no soporta nativamente tiempos de espera en las solicitudes. Sin embargo, se pueden implementar tiempos de espera utilizando la función Promise.race()
de JavaScript para mejorar la robustez de tus solicitudes de red, especialmente en entornos con condiciones de red poco confiables. Aunque el Fetch API proporciona una manera flexible y moderna de hacer solicitudes de red, no incluye soporte incorporado para tiempos de espera en las solicitudes.
Los tiempos de espera en las solicitudes son importantes para gestionar las solicitudes de red, especialmente en entornos donde las condiciones de red pueden ser poco fiables o inestables. Estos tiempos de espera ayudan a asegurar que tu aplicación se mantenga receptiva y no se quede colgada mientras espera que se complete una solicitud de red, proporcionando una mejor experiencia de usuario.
Para implementar tiempos de espera con el Fetch API, el documento sugiere usar la función Promise.race()
de JavaScript. Esta función toma un array de promesas y devuelve una promesa que se resuelve o rechaza tan pronto como una de las promesas en el array se resuelva o rechace, de ahí el nombre "race" (carrera).
Al usar Promise.race()
, puedes configurar una carrera entre la solicitud fetch y una promesa de tiempo de espera. Si la solicitud fetch se completa antes de que se cumpla el tiempo de espera, la promesa de la solicitud fetch se resolverá primero y se usará su resultado. Si el tiempo de espera ocurre antes de que se complete la solicitud fetch, la promesa de tiempo de espera se rechazará primero, permitiéndote manejar la situación de tiempo de espera según sea necesario.
Este enfoque mejora la robustez de tus solicitudes de red, dándote más control sobre su comportamiento y asegurando que tu aplicación pueda manejar una amplia gama de condiciones de red de manera efectiva. Esto es particularmente crucial en aplicaciones web modernas, donde una interacción fluida y receptiva con servidores externos y APIs es una parte clave para proporcionar una experiencia de usuario de alta calidad.
Ejemplo: Implementación de Tiempos de Espera en Fetch
function fetchWithTimeout(url, options, timeout = 5000) {
const fetchPromise = fetch(url, options);
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), timeout);
});
return Promise.race([fetchPromise, timeoutPromise]);
}
fetchWithTimeout('<https://api.example.com/data>')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Failed:', error));
Este fragmento de código de JavaScript introduce una función llamada fetchWithTimeout
, diseñada para enviar una solicitud de red a una URL específica con un tiempo de espera. Esta función es especialmente útil en escenarios donde se realizan solicitudes de red en entornos con conexiones potencialmente poco confiables o de alta latencia, y se desea evitar que la aplicación se bloquee indefinidamente esperando una respuesta que podría no llegar nunca.
Los parámetros de la función son url
, options
y timeout
. El url
es el endpoint al que se envía la solicitud, options
son los parámetros adicionales o encabezados que se desean incluir en la solicitud, y timeout
es el número máximo de milisegundos que se desea esperar por la respuesta antes de desistir. El tiempo de espera predeterminado es de 5000 milisegundos (o 5 segundos), pero se puede personalizar este valor según las necesidades.
La función funciona utilizando la API fetch
para realizar la solicitud, que es un método moderno y basado en promesas para hacer solicitudes de red en JavaScript. fetch
proporciona un enfoque más potente y flexible para realizar solicitudes HTTP en comparación con métodos más antiguos como XMLHttpRequest
.
Sin embargo, una limitación de la API fetch
es que no soporta nativamente tiempos de espera en las solicitudes. Para superar esta limitación, la función usa Promise.race
para establecer el tiempo de espera. Promise.race
es un método que toma un array de promesas y devuelve una nueva promesa que se resuelve tan pronto como una de las promesas de entrada se resuelva. En otras palabras, "compite" las promesas entre sí y proporciona el resultado de la que se resuelva más rápido.
En este caso, se está haciendo competir la solicitud fetch (que es una promesa que se resuelve cuando la solicitud se completa) contra una promesa de tiempo de espera (que es una promesa que se rechaza automáticamente después del período de tiempo especificado). Si la solicitud fetch se completa antes del tiempo de espera, la promesa fetch se resolverá primero y se usará su resultado. Si el tiempo de espera ocurre antes de que se complete la solicitud fetch, la promesa de tiempo de espera se rechazará primero, y se lanzará un Error
con el mensaje "Request timed out".
El uso de la función se demuestra en la parte final del fragmento de código. Aquí, se envía una solicitud GET
a 'https://api.example.com/data', se intenta analizar la respuesta como JSON usando el método .json()
, y luego se registran los datos resultantes en la consola si la solicitud es exitosa o se registra un mensaje de error si falla.
En este ejemplo, fetchWithTimeout
compite la promesa fetch contra una promesa de tiempo de espera, que se rechazará después de un período de tiempo especificado. Esto asegura que la aplicación pueda manejar situaciones en las que una solicitud podría colgarse más tiempo del esperado.
7.1.6 Respuestas en Streaming
El Fetch API soporta el streaming de respuestas, lo que permite empezar a procesar datos tan pronto como comienzan a llegar. Esto es particularmente útil para manejar grandes conjuntos de datos o medios en streaming.
El Fetch API es una característica poderosa que permite a los desarrolladores empezar a procesar datos tan pronto como comienzan a llegar, en lugar de tener que esperar a que el conjunto de datos completo se descargue. Esto es especialmente beneficioso cuando se manejan grandes conjuntos de datos o medios en streaming.
En los escenarios tradicionales de transferencia de datos, generalmente se tendría que esperar a que el conjunto de datos completo se descargue antes de poder empezar a procesarlo. Esto podría resultar en demoras significativas, especialmente al manejar grandes cantidades de datos o en escenarios donde la conectividad de red es deficiente.
Sin embargo, con la característica de Respuestas en Streaming del Fetch API, los datos se pueden procesar en fragmentos a medida que llegan. Esto significa que se puede comenzar a trabajar con los datos casi de inmediato, mejorando el rendimiento percibido de la aplicación y proporcionando una mejor experiencia de usuario.
Esta característica puede ser particularmente beneficiosa al desarrollar aplicaciones que necesitan manejar tareas como transmisión de video en vivo o procesamiento de datos en tiempo real, donde esperar a que se descargue el conjunto completo de datos no es práctico ni eficiente.
El Fetch API abstrae muchas de las complejidades asociadas con el streaming de datos, permitiendo a los desarrolladores centrarse en construir sus aplicaciones sin tener que preocuparse por los detalles subyacentes de la transmisión y el procesamiento de datos. Con su soporte para Respuestas en Streaming, el Fetch API es una herramienta invaluable para el desarrollo web moderno.
Ejemplo: Transmisión de una Respuesta con Fetch
async function fetchAndProcessStream(url) {
const response = await fetch(url);
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log('Received chunk', value);
// Process each chunk
}
console.log('Response fully processed');
}
fetchAndProcessStream('<https://api.example.com/large-data>');
Aquí hay un desglose paso a paso de lo que hace la función:
async function fetchAndProcessStream(url) {
: Esta línea declara una función asincrónica llamada 'fetchAndProcessStream'. La palabra clave 'async' indica que esta función devolverá una Promesa. La función toma un solo argumento 'url', que es la URL del recurso de datos que se desea obtener.const response = await fetch(url);
: Esta línea envía una solicitud fetch a la URL especificada y espera la respuesta. La palabra clave 'await' se utiliza para pausar la ejecución de la función hasta que la Promesa devuelta por el método fetch() se resuelva.const reader = response.body.getReader();
: Esta línea obtiene un flujo legible del cuerpo de la respuesta. Un flujo legible es un objeto que permite leer datos de una fuente de manera asincrónica y en streaming.while (true) {
: Esta línea inicia un bucle infinito. Este bucle continuará hasta que se rompa explícitamente.const { done, value } = await reader.read();
: Esta línea lee un fragmento de datos del flujo. El método read() devuelve una Promesa que se resuelve en un objeto. El objeto contiene dos propiedades: 'done' y 'value'. 'done' es un booleano que indica si el lector ha terminado de leer los datos, y 'value' es el fragmento de datos.if (done) break;
: Esta línea verifica si el lector ha terminado de leer los datos. Si 'done' es verdadero, el bucle se rompe y la función deja de leer datos del flujo.console.log('Received chunk', value);
: Esta línea registra cada fragmento recibido en la consola. Aquí es donde se podría agregar código para procesar cada fragmento de datos a medida que llega.console.log('Response fully processed');
: Después de que todos los fragmentos de datos se hayan recibido y procesado, esta línea registra 'Response fully processed' en la consola, indicando que toda la respuesta ha sido manejada.fetchAndProcessStream('<https://api.example.com/large-data>');
: La última línea del código es una llamada a la función fetchAndProcessStream, con la URL de un recurso de datos grande como argumento.
Esta función es particularmente útil cuando se manejan grandes conjuntos de datos o datos en streaming, ya que permite el procesamiento eficiente y en tiempo real de los datos a medida que llegan. En lugar de esperar a que se descargue todo el conjunto de datos antes de comenzar a procesarlo, esta función permite que la aplicación comience a trabajar con los datos casi de inmediato, mejorando el rendimiento percibido de la aplicación y proporcionando una mejor experiencia de usuario.
Este ejemplo de código demuestra cómo leer de una respuesta en streaming de manera incremental, lo cual puede mejorar el rendimiento percibido de tu aplicación web al manejar grandes cantidades de datos.
7.1.7 Fetch con CORS
Cross-Origin Resource Sharing (CORS) es un requisito común para aplicaciones web que realizan solicitudes a dominios diferentes del dominio de origen. Comprender cómo manejar CORS con Fetch es esencial para el desarrollo web moderno.
CORS es un mecanismo que permite o deniega a las aplicaciones web hacer solicitudes a un dominio que es diferente de su propio dominio de origen. Esto es un requisito común en el desarrollo web actual, ya que muchas aplicaciones web necesitan acceder a recursos, como fuentes, JavaScript y APIs, que están alojados en un dominio diferente.
La API Fetch es una API moderna y basada en promesas integrada en JavaScript que proporciona una manera flexible y poderosa de hacer solicitudes de red. Es una mejora con respecto al antiguo XMLHttpRequest y permite a los desarrolladores hacer solicitudes tanto a destinos de mismo origen como de origen cruzado, por lo que es una herramienta valiosa para manejar CORS.
Combinar Fetch con CORS permite a los desarrolladores hacer solicitudes de origen cruzado directamente desde sus aplicaciones web, proporcionando una forma de interactuar con otros sitios y servicios a través de sus APIs. Esto puede expandir enormemente las capacidades de una aplicación web, permitiéndole obtener datos de diversas fuentes, integrarse con otros servicios e interactuar con la web en general.
Sin embargo, al igual que con todo lo relacionado con la seguridad y la web, es importante usar estas herramientas con prudencia. CORS es una característica de seguridad diseñada para proteger a los usuarios y sus datos, por lo que es esencial entender cómo funciona y cómo usarlo correctamente. Fetch, aunque potente y flexible, es una API de bajo nivel que requiere una buena comprensión de HTTP y la política de mismo origen para ser utilizada de manera efectiva y segura.
La API Fetch y CORS son herramientas esenciales en el desarrollo web moderno. Comprender cómo funcionan juntos es clave para construir aplicaciones web sofisticadas que puedan interactuar con la web en general mientras protegen la seguridad del usuario.
Ejemplo: Fetch con CORS
fetch('<https://api.another-domain.com/data>', {
method: 'GET',
mode: 'cors', // Ensure CORS mode is set if needed
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('CORS or network error:', error));
En este ejemplo, estamos utilizando la API Fetch, una interfaz integrada en el navegador para realizar solicitudes HTTP. Está basada en promesas, lo que significa que devuelve una Promesa que se resuelve en el objeto Response que representa la respuesta a la solicitud.
Así es como funciona:
fetch('<https://api.another-domain.com/data>', {...})
: La funciónfetch()
se llama con la URL de la API a la que queremos acceder. Toma dos argumentos: la entrada y la configuración opcional (init). La entrada es la URL que estamos solicitando y la configuración es un objeto de opciones que contiene cualquier configuración personalizada que desees aplicar a la solicitud.method: 'GET'
: Esta opción indica el método de solicitud, en este caso, GET. El método GET se usa para solicitar datos de un recurso especificado.mode: 'cors'
: Este es el modo de la solicitud. Aquí se establece como 'cors', que significa Cross-Origin Resource Sharing (Intercambio de Recursos de Origen Cruzado). Este es un mecanismo que permite o bloquea los recursos solicitados en función del dominio de origen. Se necesita cuando queremos permitir solicitudes provenientes de diferentes dominios.headers: {...}
: Los encabezados de la solicitud se establecen en esta sección. El encabezado 'Content-Type' se establece en 'application/json', lo que significa que el servidor interpretará los datos enviados como un objeto JSON..then(response => response.json())
: Una vez realizada la solicitud, la API Fetch devuelve una Promesa que se resuelve en el objeto Response. El método.then()
es un método de Promesa utilizado para funciones de callback para el éxito y el fallo de las Promesas. Aquí, el objeto response se pasa a una función callback donde se convierte en formato JSON utilizando el métodojson()
..then(data => console.log(data))
: Después de la conversión a JSON, los datos se pasan a otro callback de.then()
donde se registran en la consola..catch(error => console.error('CORS or network error:', error))
: El métodocatch()
aquí se usa para capturar cualquier error que pueda ocurrir durante la operación fetch. Si ocurre un error durante la operación, se pasa a una función callback y se registra en la consola.
En resumen, este código envía una solicitud GET a la URL especificada y registra la respuesta (o cualquier error que pueda ocurrir) en la consola. El uso de promesas con los métodos .then()
y .catch()
permite manejar operaciones asíncronas, haciendo posible esperar la respuesta del servidor y manejarla una vez que esté disponible.
7.1 Fetch API para Solicitudes HTTP
Bienvenido al capítulo 7, "APIs y Interfaces Web". En este esclarecedor capítulo, profundizaremos en las versátiles interfaces y APIs que ofrecen los navegadores web actuales. Con estas APIs a nuestra disposición, nosotros, como desarrolladores web, tenemos el poder de crear aplicaciones web ricas e interactivas que aprovechan al máximo no solo las capacidades del navegador, sino también el potencial del sistema operativo subyacente.
Este capítulo promete cubrir una amplia gama de APIs web, desde aquellas principalmente involucradas en realizar solicitudes HTTP, hasta las que son expertas en manejar archivos, gestionar diversos medios e incluso aquellas que interactúan directamente con el hardware del dispositivo.
En la era digital en rápida evolución, las aplicaciones web frecuentemente necesitan establecer comunicación con servidores externos, obtener datos cruciales, enviar actualizaciones oportunas e interactuar de manera dinámica y fluida con los usuarios. Tener un sólido entendimiento y un uso efectivo de las APIs web se convierte en una habilidad crítica para construir aplicaciones responsivas. Para equiparte con esta habilidad esencial, comenzamos este capítulo con una exploración profunda del Fetch API, un enfoque contemporáneo y flexible para realizar solicitudes HTTP, diseñado para la web moderna.
El Fetch API representa una interfaz moderna y sofisticada que ofrece la capacidad de realizar solicitudes de red similares a lo que es posible con XMLHttpRequest (XHR). Sin embargo, en comparación con XMLHttpRequest, el Fetch API trae a la mesa un rango mucho más potente y flexible de características. Una de las mejoras clave del Fetch API es su uso de promesas.
Las promesas son un enfoque contemporáneo para gestionar operaciones asíncronas, que son operaciones que no tienen que completarse antes de que otro código pueda ejecutarse. Al usar promesas, el Fetch API permite que el código se escriba y lea de una manera mucho más limpia y organizada, mejorando así la eficiencia del proceso de codificación y llevando a un código más mantenible a largo plazo.
7.1.1 Uso Básico de Fetch API
La función fetch()
sirve como columna vertebral del Fetch API, una herramienta significativa en el desarrollo web moderno. Es una función increíblemente versátil que permite una amplia gama de solicitudes de red como GET, POST, PUT, DELETE, entre otros tipos de solicitud. Estas solicitudes son esenciales para interactuar con servidores y manipular datos en la web.
La solicitud GET, por ejemplo, se usa frecuentemente para recuperar datos de un servidor en formato JSON. Los datos recuperados pueden ser cualquier tipo de información que esté almacenada en el servidor. Aquí tienes una breve demostración de cómo puedes usar la función fetch()
para ejecutar una sencilla solicitud HTTP GET con el fin de obtener datos JSON de un servidor.
Ejemplo: Obtener Datos JSON
fetch('<https://api.example.com/data>')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('There was a problem with your fetch operation:', error));
Este fragmento de código demuestra el uso del Fetch API. El Fetch API utiliza promesas, un enfoque contemporáneo para gestionar operaciones asíncronas, que son operaciones que no tienen que terminar antes de que el resto del código pueda ejecutarse. Esto permite un código más eficiente, mantenible y legible.
En este ejemplo, estamos usando el Fetch API para hacer una solicitud HTTP GET a un servidor. La solicitud GET se usa a menudo para recuperar datos de un servidor, y en este caso, se espera que los datos estén en formato JSON. El servidor al que estamos solicitando datos está especificado por la URL 'https://api.example.com/data'.
La operación fetch comienza con la función fetch()
, que devuelve una promesa. Esta promesa se resuelve en el objeto Response
que representa la respuesta a la solicitud. El objeto Response
contiene información sobre la respuesta del servidor, incluida la estatus de la solicitud.
El primer método then()
en la cadena de promesas maneja la respuesta de la operación fetch. Dentro de este método, estamos verificando si la respuesta fue exitosa utilizando la propiedad ok
del objeto Response
. Esta propiedad devuelve un Booleano que indica si el estado de la respuesta está dentro del rango exitoso (200-299).
Si la respuesta no fue OK, lanzamos un Error con un mensaje personalizado que incluye el texto del estado de la respuesta. El texto del estado proporciona una explicación legible por humanos del estado de la respuesta, como 'Not Found' para un estado 404.
Si la respuesta fue OK, devolvemos el cuerpo de la respuesta analizado como JSON utilizando el método json()
. Este método lee el flujo de la respuesta hasta completarla y analiza el resultado como un objeto JSON.
El segundo método then()
en la cadena de promesas recibe los datos JSON analizados del then()
anterior. Aquí, simplemente estamos registrando los datos en la consola.
El método catch()
al final de la cadena de promesas se usa para capturar cualquier error que pueda ocurrir durante la operación fetch o durante el análisis de los datos JSON. Si se captura un error, lo registramos en la consola con un mensaje de error personalizado.
En resumen, este fragmento de código demuestra el uso básico del Fetch API para hacer una solicitud HTTP GET, verificar si la respuesta fue exitosa, analizar los datos de la respuesta como JSON y manejar cualquier error que pueda ocurrir durante la operación.
7.1.2 Realizar Solicitudes POST con Fetch
Cuando necesitas enviar datos a un servidor, un método eficiente que puedes emplear es realizar una solicitud POST utilizando el Fetch API. Este proceso implica la especificación clara del método de la solicitud como 'POST'. Además, los datos que deseas transmitir deben incluirse en el cuerpo de la solicitud.
Estos datos pueden ser de varios tipos, como JSON o datos de formulario, dependiendo de lo que el servidor esté configurado para recibir. El Fetch API hace que este proceso sea sencillo e intuitivo, simplificando la tarea de enviar datos a servidores y haciendo tus tareas de desarrollo web más eficientes.
Ejemplo: Realizar una Solicitud POST
fetch('<https://api.example.com/data>', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'John',
email: 'john@example.com'
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
Este ejemplo demuestra el uso del Fetch API para realizar una solicitud HTTP POST a una URL específica, en este caso, 'https://api.example.com/data'. El Fetch API es una API moderna basada en promesas para realizar solicitudes de red desde aplicaciones JavaScript, y ofrece más flexibilidad y características que el método más antiguo XMLHttpRequest (XHR).
El script comienza con la función fetch()
, a la cual le pasamos la URL deseada a la que queremos enviar nuestra solicitud. Inmediatamente después de la URL, se proporciona un objeto que configura los detalles específicos de nuestra solicitud. Este objeto de configuración incluye la propiedad method
configurada como 'POST', indicando que estamos enviando datos al servidor, no solo solicitando datos de él.
En la propiedad headers
del objeto de configuración, 'Content-Type' está configurado como 'application/json'. Esto le dice al servidor que estamos enviando datos en formato JSON.
La propiedad body
contiene los datos que estamos enviando al servidor, que deben ser convertidos en una cadena usando JSON.stringify()
porque HTTP es un protocolo basado en texto y requiere que cualquier dato enviado al servidor esté en formato de cadena. En este caso, un objeto que contiene las propiedades 'name' y 'email' se convierte en cadena y se incluye en el cuerpo de la solicitud.
Después de la función fetch()
, se construye una cadena de promesas para manejar la respuesta del servidor y cualquier error que pueda ocurrir durante la operación fetch. Esto se hace utilizando los métodos .then()
y .catch()
que son parte del Fetch API basado en promesas.
El primer bloque .then()
recibe la respuesta del servidor como su argumento. Dentro de este bloque, se realiza una verificación para ver si la respuesta fue exitosa utilizando la propiedad ok
del objeto response. Si la respuesta no fue correcta, se lanza un error con un mensaje personalizado y el texto del estado de la respuesta. Si la respuesta es correcta, se devuelve como JSON utilizando el método json()
del objeto response.
El segundo bloque .then()
recibe los datos JSON analizados del bloque anterior. Aquí es donde podemos interactuar con los datos devueltos por el servidor, en este caso, simplemente se registran en la consola con un mensaje de 'Éxito:'.
Finalmente, el bloque .catch()
al final de la cadena de promesas captura cualquier error que ocurra durante la operación fetch o durante el análisis de los datos JSON. Estos errores luego se registran en la consola con un mensaje de 'Error:'.
7.1.3 Manejo de Errores
El manejo efectivo de errores es crucial cuando se realizan solicitudes de red en cualquier proyecto de programación, ya que asegura la resistencia y confiabilidad de tu aplicación. En este sentido, el Fetch API es una herramienta poderosa para los desarrolladores.
El Fetch API proporciona una forma de capturar errores de red, como problemas de conectividad o errores del servidor, y manejar errores que puedan ocurrir durante el análisis de datos. Esto incluye problemas que pueden surgir al convertir los datos de respuesta en un formato utilizable. Utilizando el Fetch API, puedes implementar un mecanismo robusto de manejo de errores para tus solicitudes de red, mejorando así la experiencia del usuario.
Ejemplo: Manejo de Errores
fetch('<https://api.example.com/data>')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => {
console.error('There was a problem with your fetch operation:', error);
});
El código comienza con una llamada a la función fetch()
, pasando una URL en forma de cadena 'https://api.example.com/data'. Esto envía una solicitud GET a la URL especificada, que se espera devuelva algunos datos.
Esta función fetch()
devuelve una Promesa. Las Promesas en JavaScript representan la finalización o el fracaso de una operación asíncrona y su valor resultante. Se utilizan para manejar operaciones asíncronas como esta solicitud de red.
La Promesa devuelta se encadena con un método then()
. El método then()
toma una función de devolución de llamada que se ejecutará cuando la Promesa se resuelva con éxito. La función de devolución de llamada recibe la respuesta de la operación fetch como su argumento.
Dentro de la devolución de llamada, primero verificamos si la respuesta fue exitosa comprobando la propiedad ok
del objeto de respuesta. Si la respuesta no fue exitosa, se lanza un Error con un mensaje que dice 'La respuesta de la red no fue correcta', junto con el texto del estado de la respuesta.
Si la respuesta fue exitosa, la devolución de llamada devuelve otra Promesa llamando a response.json()
. Este método lee el flujo de la respuesta hasta completarlo y analiza el resultado como JSON.
El método then()
se encadena nuevamente para manejar el valor resuelto de la Promesa response.json()
. Esta devolución de llamada recibe los datos JSON analizados como su argumento y los registra en la consola.
Finalmente, un método catch()
se encadena al final de la cadena de Promesas. El método catch()
se utiliza para manejar cualquier rechazo de las Promesas en la cadena, incluidos los errores que puedan ocurrir durante la operación fetch o el análisis del JSON. Si se captura un error, el error se registra en la consola con un mensaje de error personalizado.
En resumen, este ejemplo de código demuestra cómo usar el Fetch API para realizar una solicitud de red, manejar la respuesta, analizar los datos de la respuesta como JSON y manejar cualquier error que pueda ocurrir durante estas operaciones.
7.1.4 Usar Async/Await con Fetch
El Fetch API, una herramienta poderosa y flexible para hacer solicitudes de red, puede usarse junto con async
y await
para crear un estilo más sincrónico de manejar operaciones asíncronas. Este enfoque nos permite escribir código que es más fácil de entender y depurar porque parece ser sincrónico, aunque en realidad se está ejecutando de forma asincrónica.
Esto es particularmente útil en escenarios donde necesitamos esperar la respuesta de una solicitud antes de hacer otra, por ejemplo, al encadenar solicitudes a APIs. Al usar async
y await
con el Fetch API, podemos simplificar en gran medida la estructura y la legibilidad de nuestro código.
El Fetch API facilita la obtención de recursos a través de la red y es una parte integral del desarrollo web moderno, permitiendo una forma más flexible y poderosa de realizar solicitudes HTTP en comparación con el XMLHttpRequest tradicional.
En JavaScript, async
y await
son palabras clave que proporcionan una forma de escribir código basado en promesas de manera más sincrónica. Permiten a los desarrolladores manejar operaciones asíncronas sin caer en el infierno de las devoluciones de llamada, mejorando la legibilidad y mantenibilidad del código.
Al usar async
y await
con el Fetch API, las operaciones asíncronas como las solicitudes de red o las operaciones de archivos pueden escribirse de una manera que parece ser bloqueante, pero en realidad no lo es. Esto significa que mientras la operación asíncrona se está procesando, el motor de JavaScript puede ejecutar otras operaciones sin ser bloqueado por la operación asíncrona pendiente.
Ejemplo: Usar Async/Await con Fetch
El uso de async
y await
con el Fetch API se ve algo así:
async function fetchData() {
try {
const response = await fetch('<https://api.example.com/data>');
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('There was a problem with your fetch operation:', error);
}
}
fetchData();
En este ejemplo, la función fetchData
se declara como async
, lo que indica que la función devolverá una promesa. Dentro de la función fetchData
, se utiliza la palabra clave await
antes del método fetch
y response.json()
. Esto le dice a JavaScript que pause la ejecución de la función fetchData
hasta que la promesa de fetch
y response.json()
se haya resuelto, y luego reanuda la ejecución y devuelve el valor resuelto.
Si ocurre un error durante la operación fetch o al analizar el JSON, se captura en el bloque catch
, evitando que el programa se bloquee y proporcionando una oportunidad para manejar el error de manera adecuada.
Combinar el Fetch API con async
y await
no solo mejora la legibilidad del código, sino que también facilita el manejo de errores y casos extremos, convirtiéndolo en una herramienta poderosa para desarrollar aplicaciones web complejas que dependen en gran medida de operaciones asíncronas.
7.1.5 Manejo de Tiempos de Espera con Fetch
El Fetch API no soporta nativamente tiempos de espera en las solicitudes. Sin embargo, se pueden implementar tiempos de espera utilizando la función Promise.race()
de JavaScript para mejorar la robustez de tus solicitudes de red, especialmente en entornos con condiciones de red poco confiables. Aunque el Fetch API proporciona una manera flexible y moderna de hacer solicitudes de red, no incluye soporte incorporado para tiempos de espera en las solicitudes.
Los tiempos de espera en las solicitudes son importantes para gestionar las solicitudes de red, especialmente en entornos donde las condiciones de red pueden ser poco fiables o inestables. Estos tiempos de espera ayudan a asegurar que tu aplicación se mantenga receptiva y no se quede colgada mientras espera que se complete una solicitud de red, proporcionando una mejor experiencia de usuario.
Para implementar tiempos de espera con el Fetch API, el documento sugiere usar la función Promise.race()
de JavaScript. Esta función toma un array de promesas y devuelve una promesa que se resuelve o rechaza tan pronto como una de las promesas en el array se resuelva o rechace, de ahí el nombre "race" (carrera).
Al usar Promise.race()
, puedes configurar una carrera entre la solicitud fetch y una promesa de tiempo de espera. Si la solicitud fetch se completa antes de que se cumpla el tiempo de espera, la promesa de la solicitud fetch se resolverá primero y se usará su resultado. Si el tiempo de espera ocurre antes de que se complete la solicitud fetch, la promesa de tiempo de espera se rechazará primero, permitiéndote manejar la situación de tiempo de espera según sea necesario.
Este enfoque mejora la robustez de tus solicitudes de red, dándote más control sobre su comportamiento y asegurando que tu aplicación pueda manejar una amplia gama de condiciones de red de manera efectiva. Esto es particularmente crucial en aplicaciones web modernas, donde una interacción fluida y receptiva con servidores externos y APIs es una parte clave para proporcionar una experiencia de usuario de alta calidad.
Ejemplo: Implementación de Tiempos de Espera en Fetch
function fetchWithTimeout(url, options, timeout = 5000) {
const fetchPromise = fetch(url, options);
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), timeout);
});
return Promise.race([fetchPromise, timeoutPromise]);
}
fetchWithTimeout('<https://api.example.com/data>')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Failed:', error));
Este fragmento de código de JavaScript introduce una función llamada fetchWithTimeout
, diseñada para enviar una solicitud de red a una URL específica con un tiempo de espera. Esta función es especialmente útil en escenarios donde se realizan solicitudes de red en entornos con conexiones potencialmente poco confiables o de alta latencia, y se desea evitar que la aplicación se bloquee indefinidamente esperando una respuesta que podría no llegar nunca.
Los parámetros de la función son url
, options
y timeout
. El url
es el endpoint al que se envía la solicitud, options
son los parámetros adicionales o encabezados que se desean incluir en la solicitud, y timeout
es el número máximo de milisegundos que se desea esperar por la respuesta antes de desistir. El tiempo de espera predeterminado es de 5000 milisegundos (o 5 segundos), pero se puede personalizar este valor según las necesidades.
La función funciona utilizando la API fetch
para realizar la solicitud, que es un método moderno y basado en promesas para hacer solicitudes de red en JavaScript. fetch
proporciona un enfoque más potente y flexible para realizar solicitudes HTTP en comparación con métodos más antiguos como XMLHttpRequest
.
Sin embargo, una limitación de la API fetch
es que no soporta nativamente tiempos de espera en las solicitudes. Para superar esta limitación, la función usa Promise.race
para establecer el tiempo de espera. Promise.race
es un método que toma un array de promesas y devuelve una nueva promesa que se resuelve tan pronto como una de las promesas de entrada se resuelva. En otras palabras, "compite" las promesas entre sí y proporciona el resultado de la que se resuelva más rápido.
En este caso, se está haciendo competir la solicitud fetch (que es una promesa que se resuelve cuando la solicitud se completa) contra una promesa de tiempo de espera (que es una promesa que se rechaza automáticamente después del período de tiempo especificado). Si la solicitud fetch se completa antes del tiempo de espera, la promesa fetch se resolverá primero y se usará su resultado. Si el tiempo de espera ocurre antes de que se complete la solicitud fetch, la promesa de tiempo de espera se rechazará primero, y se lanzará un Error
con el mensaje "Request timed out".
El uso de la función se demuestra en la parte final del fragmento de código. Aquí, se envía una solicitud GET
a 'https://api.example.com/data', se intenta analizar la respuesta como JSON usando el método .json()
, y luego se registran los datos resultantes en la consola si la solicitud es exitosa o se registra un mensaje de error si falla.
En este ejemplo, fetchWithTimeout
compite la promesa fetch contra una promesa de tiempo de espera, que se rechazará después de un período de tiempo especificado. Esto asegura que la aplicación pueda manejar situaciones en las que una solicitud podría colgarse más tiempo del esperado.
7.1.6 Respuestas en Streaming
El Fetch API soporta el streaming de respuestas, lo que permite empezar a procesar datos tan pronto como comienzan a llegar. Esto es particularmente útil para manejar grandes conjuntos de datos o medios en streaming.
El Fetch API es una característica poderosa que permite a los desarrolladores empezar a procesar datos tan pronto como comienzan a llegar, en lugar de tener que esperar a que el conjunto de datos completo se descargue. Esto es especialmente beneficioso cuando se manejan grandes conjuntos de datos o medios en streaming.
En los escenarios tradicionales de transferencia de datos, generalmente se tendría que esperar a que el conjunto de datos completo se descargue antes de poder empezar a procesarlo. Esto podría resultar en demoras significativas, especialmente al manejar grandes cantidades de datos o en escenarios donde la conectividad de red es deficiente.
Sin embargo, con la característica de Respuestas en Streaming del Fetch API, los datos se pueden procesar en fragmentos a medida que llegan. Esto significa que se puede comenzar a trabajar con los datos casi de inmediato, mejorando el rendimiento percibido de la aplicación y proporcionando una mejor experiencia de usuario.
Esta característica puede ser particularmente beneficiosa al desarrollar aplicaciones que necesitan manejar tareas como transmisión de video en vivo o procesamiento de datos en tiempo real, donde esperar a que se descargue el conjunto completo de datos no es práctico ni eficiente.
El Fetch API abstrae muchas de las complejidades asociadas con el streaming de datos, permitiendo a los desarrolladores centrarse en construir sus aplicaciones sin tener que preocuparse por los detalles subyacentes de la transmisión y el procesamiento de datos. Con su soporte para Respuestas en Streaming, el Fetch API es una herramienta invaluable para el desarrollo web moderno.
Ejemplo: Transmisión de una Respuesta con Fetch
async function fetchAndProcessStream(url) {
const response = await fetch(url);
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log('Received chunk', value);
// Process each chunk
}
console.log('Response fully processed');
}
fetchAndProcessStream('<https://api.example.com/large-data>');
Aquí hay un desglose paso a paso de lo que hace la función:
async function fetchAndProcessStream(url) {
: Esta línea declara una función asincrónica llamada 'fetchAndProcessStream'. La palabra clave 'async' indica que esta función devolverá una Promesa. La función toma un solo argumento 'url', que es la URL del recurso de datos que se desea obtener.const response = await fetch(url);
: Esta línea envía una solicitud fetch a la URL especificada y espera la respuesta. La palabra clave 'await' se utiliza para pausar la ejecución de la función hasta que la Promesa devuelta por el método fetch() se resuelva.const reader = response.body.getReader();
: Esta línea obtiene un flujo legible del cuerpo de la respuesta. Un flujo legible es un objeto que permite leer datos de una fuente de manera asincrónica y en streaming.while (true) {
: Esta línea inicia un bucle infinito. Este bucle continuará hasta que se rompa explícitamente.const { done, value } = await reader.read();
: Esta línea lee un fragmento de datos del flujo. El método read() devuelve una Promesa que se resuelve en un objeto. El objeto contiene dos propiedades: 'done' y 'value'. 'done' es un booleano que indica si el lector ha terminado de leer los datos, y 'value' es el fragmento de datos.if (done) break;
: Esta línea verifica si el lector ha terminado de leer los datos. Si 'done' es verdadero, el bucle se rompe y la función deja de leer datos del flujo.console.log('Received chunk', value);
: Esta línea registra cada fragmento recibido en la consola. Aquí es donde se podría agregar código para procesar cada fragmento de datos a medida que llega.console.log('Response fully processed');
: Después de que todos los fragmentos de datos se hayan recibido y procesado, esta línea registra 'Response fully processed' en la consola, indicando que toda la respuesta ha sido manejada.fetchAndProcessStream('<https://api.example.com/large-data>');
: La última línea del código es una llamada a la función fetchAndProcessStream, con la URL de un recurso de datos grande como argumento.
Esta función es particularmente útil cuando se manejan grandes conjuntos de datos o datos en streaming, ya que permite el procesamiento eficiente y en tiempo real de los datos a medida que llegan. En lugar de esperar a que se descargue todo el conjunto de datos antes de comenzar a procesarlo, esta función permite que la aplicación comience a trabajar con los datos casi de inmediato, mejorando el rendimiento percibido de la aplicación y proporcionando una mejor experiencia de usuario.
Este ejemplo de código demuestra cómo leer de una respuesta en streaming de manera incremental, lo cual puede mejorar el rendimiento percibido de tu aplicación web al manejar grandes cantidades de datos.
7.1.7 Fetch con CORS
Cross-Origin Resource Sharing (CORS) es un requisito común para aplicaciones web que realizan solicitudes a dominios diferentes del dominio de origen. Comprender cómo manejar CORS con Fetch es esencial para el desarrollo web moderno.
CORS es un mecanismo que permite o deniega a las aplicaciones web hacer solicitudes a un dominio que es diferente de su propio dominio de origen. Esto es un requisito común en el desarrollo web actual, ya que muchas aplicaciones web necesitan acceder a recursos, como fuentes, JavaScript y APIs, que están alojados en un dominio diferente.
La API Fetch es una API moderna y basada en promesas integrada en JavaScript que proporciona una manera flexible y poderosa de hacer solicitudes de red. Es una mejora con respecto al antiguo XMLHttpRequest y permite a los desarrolladores hacer solicitudes tanto a destinos de mismo origen como de origen cruzado, por lo que es una herramienta valiosa para manejar CORS.
Combinar Fetch con CORS permite a los desarrolladores hacer solicitudes de origen cruzado directamente desde sus aplicaciones web, proporcionando una forma de interactuar con otros sitios y servicios a través de sus APIs. Esto puede expandir enormemente las capacidades de una aplicación web, permitiéndole obtener datos de diversas fuentes, integrarse con otros servicios e interactuar con la web en general.
Sin embargo, al igual que con todo lo relacionado con la seguridad y la web, es importante usar estas herramientas con prudencia. CORS es una característica de seguridad diseñada para proteger a los usuarios y sus datos, por lo que es esencial entender cómo funciona y cómo usarlo correctamente. Fetch, aunque potente y flexible, es una API de bajo nivel que requiere una buena comprensión de HTTP y la política de mismo origen para ser utilizada de manera efectiva y segura.
La API Fetch y CORS son herramientas esenciales en el desarrollo web moderno. Comprender cómo funcionan juntos es clave para construir aplicaciones web sofisticadas que puedan interactuar con la web en general mientras protegen la seguridad del usuario.
Ejemplo: Fetch con CORS
fetch('<https://api.another-domain.com/data>', {
method: 'GET',
mode: 'cors', // Ensure CORS mode is set if needed
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('CORS or network error:', error));
En este ejemplo, estamos utilizando la API Fetch, una interfaz integrada en el navegador para realizar solicitudes HTTP. Está basada en promesas, lo que significa que devuelve una Promesa que se resuelve en el objeto Response que representa la respuesta a la solicitud.
Así es como funciona:
fetch('<https://api.another-domain.com/data>', {...})
: La funciónfetch()
se llama con la URL de la API a la que queremos acceder. Toma dos argumentos: la entrada y la configuración opcional (init). La entrada es la URL que estamos solicitando y la configuración es un objeto de opciones que contiene cualquier configuración personalizada que desees aplicar a la solicitud.method: 'GET'
: Esta opción indica el método de solicitud, en este caso, GET. El método GET se usa para solicitar datos de un recurso especificado.mode: 'cors'
: Este es el modo de la solicitud. Aquí se establece como 'cors', que significa Cross-Origin Resource Sharing (Intercambio de Recursos de Origen Cruzado). Este es un mecanismo que permite o bloquea los recursos solicitados en función del dominio de origen. Se necesita cuando queremos permitir solicitudes provenientes de diferentes dominios.headers: {...}
: Los encabezados de la solicitud se establecen en esta sección. El encabezado 'Content-Type' se establece en 'application/json', lo que significa que el servidor interpretará los datos enviados como un objeto JSON..then(response => response.json())
: Una vez realizada la solicitud, la API Fetch devuelve una Promesa que se resuelve en el objeto Response. El método.then()
es un método de Promesa utilizado para funciones de callback para el éxito y el fallo de las Promesas. Aquí, el objeto response se pasa a una función callback donde se convierte en formato JSON utilizando el métodojson()
..then(data => console.log(data))
: Después de la conversión a JSON, los datos se pasan a otro callback de.then()
donde se registran en la consola..catch(error => console.error('CORS or network error:', error))
: El métodocatch()
aquí se usa para capturar cualquier error que pueda ocurrir durante la operación fetch. Si ocurre un error durante la operación, se pasa a una función callback y se registra en la consola.
En resumen, este código envía una solicitud GET a la URL especificada y registra la respuesta (o cualquier error que pueda ocurrir) en la consola. El uso de promesas con los métodos .then()
y .catch()
permite manejar operaciones asíncronas, haciendo posible esperar la respuesta del servidor y manejarla una vez que esté disponible.
7.1 Fetch API para Solicitudes HTTP
Bienvenido al capítulo 7, "APIs y Interfaces Web". En este esclarecedor capítulo, profundizaremos en las versátiles interfaces y APIs que ofrecen los navegadores web actuales. Con estas APIs a nuestra disposición, nosotros, como desarrolladores web, tenemos el poder de crear aplicaciones web ricas e interactivas que aprovechan al máximo no solo las capacidades del navegador, sino también el potencial del sistema operativo subyacente.
Este capítulo promete cubrir una amplia gama de APIs web, desde aquellas principalmente involucradas en realizar solicitudes HTTP, hasta las que son expertas en manejar archivos, gestionar diversos medios e incluso aquellas que interactúan directamente con el hardware del dispositivo.
En la era digital en rápida evolución, las aplicaciones web frecuentemente necesitan establecer comunicación con servidores externos, obtener datos cruciales, enviar actualizaciones oportunas e interactuar de manera dinámica y fluida con los usuarios. Tener un sólido entendimiento y un uso efectivo de las APIs web se convierte en una habilidad crítica para construir aplicaciones responsivas. Para equiparte con esta habilidad esencial, comenzamos este capítulo con una exploración profunda del Fetch API, un enfoque contemporáneo y flexible para realizar solicitudes HTTP, diseñado para la web moderna.
El Fetch API representa una interfaz moderna y sofisticada que ofrece la capacidad de realizar solicitudes de red similares a lo que es posible con XMLHttpRequest (XHR). Sin embargo, en comparación con XMLHttpRequest, el Fetch API trae a la mesa un rango mucho más potente y flexible de características. Una de las mejoras clave del Fetch API es su uso de promesas.
Las promesas son un enfoque contemporáneo para gestionar operaciones asíncronas, que son operaciones que no tienen que completarse antes de que otro código pueda ejecutarse. Al usar promesas, el Fetch API permite que el código se escriba y lea de una manera mucho más limpia y organizada, mejorando así la eficiencia del proceso de codificación y llevando a un código más mantenible a largo plazo.
7.1.1 Uso Básico de Fetch API
La función fetch()
sirve como columna vertebral del Fetch API, una herramienta significativa en el desarrollo web moderno. Es una función increíblemente versátil que permite una amplia gama de solicitudes de red como GET, POST, PUT, DELETE, entre otros tipos de solicitud. Estas solicitudes son esenciales para interactuar con servidores y manipular datos en la web.
La solicitud GET, por ejemplo, se usa frecuentemente para recuperar datos de un servidor en formato JSON. Los datos recuperados pueden ser cualquier tipo de información que esté almacenada en el servidor. Aquí tienes una breve demostración de cómo puedes usar la función fetch()
para ejecutar una sencilla solicitud HTTP GET con el fin de obtener datos JSON de un servidor.
Ejemplo: Obtener Datos JSON
fetch('<https://api.example.com/data>')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('There was a problem with your fetch operation:', error));
Este fragmento de código demuestra el uso del Fetch API. El Fetch API utiliza promesas, un enfoque contemporáneo para gestionar operaciones asíncronas, que son operaciones que no tienen que terminar antes de que el resto del código pueda ejecutarse. Esto permite un código más eficiente, mantenible y legible.
En este ejemplo, estamos usando el Fetch API para hacer una solicitud HTTP GET a un servidor. La solicitud GET se usa a menudo para recuperar datos de un servidor, y en este caso, se espera que los datos estén en formato JSON. El servidor al que estamos solicitando datos está especificado por la URL 'https://api.example.com/data'.
La operación fetch comienza con la función fetch()
, que devuelve una promesa. Esta promesa se resuelve en el objeto Response
que representa la respuesta a la solicitud. El objeto Response
contiene información sobre la respuesta del servidor, incluida la estatus de la solicitud.
El primer método then()
en la cadena de promesas maneja la respuesta de la operación fetch. Dentro de este método, estamos verificando si la respuesta fue exitosa utilizando la propiedad ok
del objeto Response
. Esta propiedad devuelve un Booleano que indica si el estado de la respuesta está dentro del rango exitoso (200-299).
Si la respuesta no fue OK, lanzamos un Error con un mensaje personalizado que incluye el texto del estado de la respuesta. El texto del estado proporciona una explicación legible por humanos del estado de la respuesta, como 'Not Found' para un estado 404.
Si la respuesta fue OK, devolvemos el cuerpo de la respuesta analizado como JSON utilizando el método json()
. Este método lee el flujo de la respuesta hasta completarla y analiza el resultado como un objeto JSON.
El segundo método then()
en la cadena de promesas recibe los datos JSON analizados del then()
anterior. Aquí, simplemente estamos registrando los datos en la consola.
El método catch()
al final de la cadena de promesas se usa para capturar cualquier error que pueda ocurrir durante la operación fetch o durante el análisis de los datos JSON. Si se captura un error, lo registramos en la consola con un mensaje de error personalizado.
En resumen, este fragmento de código demuestra el uso básico del Fetch API para hacer una solicitud HTTP GET, verificar si la respuesta fue exitosa, analizar los datos de la respuesta como JSON y manejar cualquier error que pueda ocurrir durante la operación.
7.1.2 Realizar Solicitudes POST con Fetch
Cuando necesitas enviar datos a un servidor, un método eficiente que puedes emplear es realizar una solicitud POST utilizando el Fetch API. Este proceso implica la especificación clara del método de la solicitud como 'POST'. Además, los datos que deseas transmitir deben incluirse en el cuerpo de la solicitud.
Estos datos pueden ser de varios tipos, como JSON o datos de formulario, dependiendo de lo que el servidor esté configurado para recibir. El Fetch API hace que este proceso sea sencillo e intuitivo, simplificando la tarea de enviar datos a servidores y haciendo tus tareas de desarrollo web más eficientes.
Ejemplo: Realizar una Solicitud POST
fetch('<https://api.example.com/data>', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'John',
email: 'john@example.com'
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
Este ejemplo demuestra el uso del Fetch API para realizar una solicitud HTTP POST a una URL específica, en este caso, 'https://api.example.com/data'. El Fetch API es una API moderna basada en promesas para realizar solicitudes de red desde aplicaciones JavaScript, y ofrece más flexibilidad y características que el método más antiguo XMLHttpRequest (XHR).
El script comienza con la función fetch()
, a la cual le pasamos la URL deseada a la que queremos enviar nuestra solicitud. Inmediatamente después de la URL, se proporciona un objeto que configura los detalles específicos de nuestra solicitud. Este objeto de configuración incluye la propiedad method
configurada como 'POST', indicando que estamos enviando datos al servidor, no solo solicitando datos de él.
En la propiedad headers
del objeto de configuración, 'Content-Type' está configurado como 'application/json'. Esto le dice al servidor que estamos enviando datos en formato JSON.
La propiedad body
contiene los datos que estamos enviando al servidor, que deben ser convertidos en una cadena usando JSON.stringify()
porque HTTP es un protocolo basado en texto y requiere que cualquier dato enviado al servidor esté en formato de cadena. En este caso, un objeto que contiene las propiedades 'name' y 'email' se convierte en cadena y se incluye en el cuerpo de la solicitud.
Después de la función fetch()
, se construye una cadena de promesas para manejar la respuesta del servidor y cualquier error que pueda ocurrir durante la operación fetch. Esto se hace utilizando los métodos .then()
y .catch()
que son parte del Fetch API basado en promesas.
El primer bloque .then()
recibe la respuesta del servidor como su argumento. Dentro de este bloque, se realiza una verificación para ver si la respuesta fue exitosa utilizando la propiedad ok
del objeto response. Si la respuesta no fue correcta, se lanza un error con un mensaje personalizado y el texto del estado de la respuesta. Si la respuesta es correcta, se devuelve como JSON utilizando el método json()
del objeto response.
El segundo bloque .then()
recibe los datos JSON analizados del bloque anterior. Aquí es donde podemos interactuar con los datos devueltos por el servidor, en este caso, simplemente se registran en la consola con un mensaje de 'Éxito:'.
Finalmente, el bloque .catch()
al final de la cadena de promesas captura cualquier error que ocurra durante la operación fetch o durante el análisis de los datos JSON. Estos errores luego se registran en la consola con un mensaje de 'Error:'.
7.1.3 Manejo de Errores
El manejo efectivo de errores es crucial cuando se realizan solicitudes de red en cualquier proyecto de programación, ya que asegura la resistencia y confiabilidad de tu aplicación. En este sentido, el Fetch API es una herramienta poderosa para los desarrolladores.
El Fetch API proporciona una forma de capturar errores de red, como problemas de conectividad o errores del servidor, y manejar errores que puedan ocurrir durante el análisis de datos. Esto incluye problemas que pueden surgir al convertir los datos de respuesta en un formato utilizable. Utilizando el Fetch API, puedes implementar un mecanismo robusto de manejo de errores para tus solicitudes de red, mejorando así la experiencia del usuario.
Ejemplo: Manejo de Errores
fetch('<https://api.example.com/data>')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => {
console.error('There was a problem with your fetch operation:', error);
});
El código comienza con una llamada a la función fetch()
, pasando una URL en forma de cadena 'https://api.example.com/data'. Esto envía una solicitud GET a la URL especificada, que se espera devuelva algunos datos.
Esta función fetch()
devuelve una Promesa. Las Promesas en JavaScript representan la finalización o el fracaso de una operación asíncrona y su valor resultante. Se utilizan para manejar operaciones asíncronas como esta solicitud de red.
La Promesa devuelta se encadena con un método then()
. El método then()
toma una función de devolución de llamada que se ejecutará cuando la Promesa se resuelva con éxito. La función de devolución de llamada recibe la respuesta de la operación fetch como su argumento.
Dentro de la devolución de llamada, primero verificamos si la respuesta fue exitosa comprobando la propiedad ok
del objeto de respuesta. Si la respuesta no fue exitosa, se lanza un Error con un mensaje que dice 'La respuesta de la red no fue correcta', junto con el texto del estado de la respuesta.
Si la respuesta fue exitosa, la devolución de llamada devuelve otra Promesa llamando a response.json()
. Este método lee el flujo de la respuesta hasta completarlo y analiza el resultado como JSON.
El método then()
se encadena nuevamente para manejar el valor resuelto de la Promesa response.json()
. Esta devolución de llamada recibe los datos JSON analizados como su argumento y los registra en la consola.
Finalmente, un método catch()
se encadena al final de la cadena de Promesas. El método catch()
se utiliza para manejar cualquier rechazo de las Promesas en la cadena, incluidos los errores que puedan ocurrir durante la operación fetch o el análisis del JSON. Si se captura un error, el error se registra en la consola con un mensaje de error personalizado.
En resumen, este ejemplo de código demuestra cómo usar el Fetch API para realizar una solicitud de red, manejar la respuesta, analizar los datos de la respuesta como JSON y manejar cualquier error que pueda ocurrir durante estas operaciones.
7.1.4 Usar Async/Await con Fetch
El Fetch API, una herramienta poderosa y flexible para hacer solicitudes de red, puede usarse junto con async
y await
para crear un estilo más sincrónico de manejar operaciones asíncronas. Este enfoque nos permite escribir código que es más fácil de entender y depurar porque parece ser sincrónico, aunque en realidad se está ejecutando de forma asincrónica.
Esto es particularmente útil en escenarios donde necesitamos esperar la respuesta de una solicitud antes de hacer otra, por ejemplo, al encadenar solicitudes a APIs. Al usar async
y await
con el Fetch API, podemos simplificar en gran medida la estructura y la legibilidad de nuestro código.
El Fetch API facilita la obtención de recursos a través de la red y es una parte integral del desarrollo web moderno, permitiendo una forma más flexible y poderosa de realizar solicitudes HTTP en comparación con el XMLHttpRequest tradicional.
En JavaScript, async
y await
son palabras clave que proporcionan una forma de escribir código basado en promesas de manera más sincrónica. Permiten a los desarrolladores manejar operaciones asíncronas sin caer en el infierno de las devoluciones de llamada, mejorando la legibilidad y mantenibilidad del código.
Al usar async
y await
con el Fetch API, las operaciones asíncronas como las solicitudes de red o las operaciones de archivos pueden escribirse de una manera que parece ser bloqueante, pero en realidad no lo es. Esto significa que mientras la operación asíncrona se está procesando, el motor de JavaScript puede ejecutar otras operaciones sin ser bloqueado por la operación asíncrona pendiente.
Ejemplo: Usar Async/Await con Fetch
El uso de async
y await
con el Fetch API se ve algo así:
async function fetchData() {
try {
const response = await fetch('<https://api.example.com/data>');
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('There was a problem with your fetch operation:', error);
}
}
fetchData();
En este ejemplo, la función fetchData
se declara como async
, lo que indica que la función devolverá una promesa. Dentro de la función fetchData
, se utiliza la palabra clave await
antes del método fetch
y response.json()
. Esto le dice a JavaScript que pause la ejecución de la función fetchData
hasta que la promesa de fetch
y response.json()
se haya resuelto, y luego reanuda la ejecución y devuelve el valor resuelto.
Si ocurre un error durante la operación fetch o al analizar el JSON, se captura en el bloque catch
, evitando que el programa se bloquee y proporcionando una oportunidad para manejar el error de manera adecuada.
Combinar el Fetch API con async
y await
no solo mejora la legibilidad del código, sino que también facilita el manejo de errores y casos extremos, convirtiéndolo en una herramienta poderosa para desarrollar aplicaciones web complejas que dependen en gran medida de operaciones asíncronas.
7.1.5 Manejo de Tiempos de Espera con Fetch
El Fetch API no soporta nativamente tiempos de espera en las solicitudes. Sin embargo, se pueden implementar tiempos de espera utilizando la función Promise.race()
de JavaScript para mejorar la robustez de tus solicitudes de red, especialmente en entornos con condiciones de red poco confiables. Aunque el Fetch API proporciona una manera flexible y moderna de hacer solicitudes de red, no incluye soporte incorporado para tiempos de espera en las solicitudes.
Los tiempos de espera en las solicitudes son importantes para gestionar las solicitudes de red, especialmente en entornos donde las condiciones de red pueden ser poco fiables o inestables. Estos tiempos de espera ayudan a asegurar que tu aplicación se mantenga receptiva y no se quede colgada mientras espera que se complete una solicitud de red, proporcionando una mejor experiencia de usuario.
Para implementar tiempos de espera con el Fetch API, el documento sugiere usar la función Promise.race()
de JavaScript. Esta función toma un array de promesas y devuelve una promesa que se resuelve o rechaza tan pronto como una de las promesas en el array se resuelva o rechace, de ahí el nombre "race" (carrera).
Al usar Promise.race()
, puedes configurar una carrera entre la solicitud fetch y una promesa de tiempo de espera. Si la solicitud fetch se completa antes de que se cumpla el tiempo de espera, la promesa de la solicitud fetch se resolverá primero y se usará su resultado. Si el tiempo de espera ocurre antes de que se complete la solicitud fetch, la promesa de tiempo de espera se rechazará primero, permitiéndote manejar la situación de tiempo de espera según sea necesario.
Este enfoque mejora la robustez de tus solicitudes de red, dándote más control sobre su comportamiento y asegurando que tu aplicación pueda manejar una amplia gama de condiciones de red de manera efectiva. Esto es particularmente crucial en aplicaciones web modernas, donde una interacción fluida y receptiva con servidores externos y APIs es una parte clave para proporcionar una experiencia de usuario de alta calidad.
Ejemplo: Implementación de Tiempos de Espera en Fetch
function fetchWithTimeout(url, options, timeout = 5000) {
const fetchPromise = fetch(url, options);
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), timeout);
});
return Promise.race([fetchPromise, timeoutPromise]);
}
fetchWithTimeout('<https://api.example.com/data>')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Failed:', error));
Este fragmento de código de JavaScript introduce una función llamada fetchWithTimeout
, diseñada para enviar una solicitud de red a una URL específica con un tiempo de espera. Esta función es especialmente útil en escenarios donde se realizan solicitudes de red en entornos con conexiones potencialmente poco confiables o de alta latencia, y se desea evitar que la aplicación se bloquee indefinidamente esperando una respuesta que podría no llegar nunca.
Los parámetros de la función son url
, options
y timeout
. El url
es el endpoint al que se envía la solicitud, options
son los parámetros adicionales o encabezados que se desean incluir en la solicitud, y timeout
es el número máximo de milisegundos que se desea esperar por la respuesta antes de desistir. El tiempo de espera predeterminado es de 5000 milisegundos (o 5 segundos), pero se puede personalizar este valor según las necesidades.
La función funciona utilizando la API fetch
para realizar la solicitud, que es un método moderno y basado en promesas para hacer solicitudes de red en JavaScript. fetch
proporciona un enfoque más potente y flexible para realizar solicitudes HTTP en comparación con métodos más antiguos como XMLHttpRequest
.
Sin embargo, una limitación de la API fetch
es que no soporta nativamente tiempos de espera en las solicitudes. Para superar esta limitación, la función usa Promise.race
para establecer el tiempo de espera. Promise.race
es un método que toma un array de promesas y devuelve una nueva promesa que se resuelve tan pronto como una de las promesas de entrada se resuelva. En otras palabras, "compite" las promesas entre sí y proporciona el resultado de la que se resuelva más rápido.
En este caso, se está haciendo competir la solicitud fetch (que es una promesa que se resuelve cuando la solicitud se completa) contra una promesa de tiempo de espera (que es una promesa que se rechaza automáticamente después del período de tiempo especificado). Si la solicitud fetch se completa antes del tiempo de espera, la promesa fetch se resolverá primero y se usará su resultado. Si el tiempo de espera ocurre antes de que se complete la solicitud fetch, la promesa de tiempo de espera se rechazará primero, y se lanzará un Error
con el mensaje "Request timed out".
El uso de la función se demuestra en la parte final del fragmento de código. Aquí, se envía una solicitud GET
a 'https://api.example.com/data', se intenta analizar la respuesta como JSON usando el método .json()
, y luego se registran los datos resultantes en la consola si la solicitud es exitosa o se registra un mensaje de error si falla.
En este ejemplo, fetchWithTimeout
compite la promesa fetch contra una promesa de tiempo de espera, que se rechazará después de un período de tiempo especificado. Esto asegura que la aplicación pueda manejar situaciones en las que una solicitud podría colgarse más tiempo del esperado.
7.1.6 Respuestas en Streaming
El Fetch API soporta el streaming de respuestas, lo que permite empezar a procesar datos tan pronto como comienzan a llegar. Esto es particularmente útil para manejar grandes conjuntos de datos o medios en streaming.
El Fetch API es una característica poderosa que permite a los desarrolladores empezar a procesar datos tan pronto como comienzan a llegar, en lugar de tener que esperar a que el conjunto de datos completo se descargue. Esto es especialmente beneficioso cuando se manejan grandes conjuntos de datos o medios en streaming.
En los escenarios tradicionales de transferencia de datos, generalmente se tendría que esperar a que el conjunto de datos completo se descargue antes de poder empezar a procesarlo. Esto podría resultar en demoras significativas, especialmente al manejar grandes cantidades de datos o en escenarios donde la conectividad de red es deficiente.
Sin embargo, con la característica de Respuestas en Streaming del Fetch API, los datos se pueden procesar en fragmentos a medida que llegan. Esto significa que se puede comenzar a trabajar con los datos casi de inmediato, mejorando el rendimiento percibido de la aplicación y proporcionando una mejor experiencia de usuario.
Esta característica puede ser particularmente beneficiosa al desarrollar aplicaciones que necesitan manejar tareas como transmisión de video en vivo o procesamiento de datos en tiempo real, donde esperar a que se descargue el conjunto completo de datos no es práctico ni eficiente.
El Fetch API abstrae muchas de las complejidades asociadas con el streaming de datos, permitiendo a los desarrolladores centrarse en construir sus aplicaciones sin tener que preocuparse por los detalles subyacentes de la transmisión y el procesamiento de datos. Con su soporte para Respuestas en Streaming, el Fetch API es una herramienta invaluable para el desarrollo web moderno.
Ejemplo: Transmisión de una Respuesta con Fetch
async function fetchAndProcessStream(url) {
const response = await fetch(url);
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log('Received chunk', value);
// Process each chunk
}
console.log('Response fully processed');
}
fetchAndProcessStream('<https://api.example.com/large-data>');
Aquí hay un desglose paso a paso de lo que hace la función:
async function fetchAndProcessStream(url) {
: Esta línea declara una función asincrónica llamada 'fetchAndProcessStream'. La palabra clave 'async' indica que esta función devolverá una Promesa. La función toma un solo argumento 'url', que es la URL del recurso de datos que se desea obtener.const response = await fetch(url);
: Esta línea envía una solicitud fetch a la URL especificada y espera la respuesta. La palabra clave 'await' se utiliza para pausar la ejecución de la función hasta que la Promesa devuelta por el método fetch() se resuelva.const reader = response.body.getReader();
: Esta línea obtiene un flujo legible del cuerpo de la respuesta. Un flujo legible es un objeto que permite leer datos de una fuente de manera asincrónica y en streaming.while (true) {
: Esta línea inicia un bucle infinito. Este bucle continuará hasta que se rompa explícitamente.const { done, value } = await reader.read();
: Esta línea lee un fragmento de datos del flujo. El método read() devuelve una Promesa que se resuelve en un objeto. El objeto contiene dos propiedades: 'done' y 'value'. 'done' es un booleano que indica si el lector ha terminado de leer los datos, y 'value' es el fragmento de datos.if (done) break;
: Esta línea verifica si el lector ha terminado de leer los datos. Si 'done' es verdadero, el bucle se rompe y la función deja de leer datos del flujo.console.log('Received chunk', value);
: Esta línea registra cada fragmento recibido en la consola. Aquí es donde se podría agregar código para procesar cada fragmento de datos a medida que llega.console.log('Response fully processed');
: Después de que todos los fragmentos de datos se hayan recibido y procesado, esta línea registra 'Response fully processed' en la consola, indicando que toda la respuesta ha sido manejada.fetchAndProcessStream('<https://api.example.com/large-data>');
: La última línea del código es una llamada a la función fetchAndProcessStream, con la URL de un recurso de datos grande como argumento.
Esta función es particularmente útil cuando se manejan grandes conjuntos de datos o datos en streaming, ya que permite el procesamiento eficiente y en tiempo real de los datos a medida que llegan. En lugar de esperar a que se descargue todo el conjunto de datos antes de comenzar a procesarlo, esta función permite que la aplicación comience a trabajar con los datos casi de inmediato, mejorando el rendimiento percibido de la aplicación y proporcionando una mejor experiencia de usuario.
Este ejemplo de código demuestra cómo leer de una respuesta en streaming de manera incremental, lo cual puede mejorar el rendimiento percibido de tu aplicación web al manejar grandes cantidades de datos.
7.1.7 Fetch con CORS
Cross-Origin Resource Sharing (CORS) es un requisito común para aplicaciones web que realizan solicitudes a dominios diferentes del dominio de origen. Comprender cómo manejar CORS con Fetch es esencial para el desarrollo web moderno.
CORS es un mecanismo que permite o deniega a las aplicaciones web hacer solicitudes a un dominio que es diferente de su propio dominio de origen. Esto es un requisito común en el desarrollo web actual, ya que muchas aplicaciones web necesitan acceder a recursos, como fuentes, JavaScript y APIs, que están alojados en un dominio diferente.
La API Fetch es una API moderna y basada en promesas integrada en JavaScript que proporciona una manera flexible y poderosa de hacer solicitudes de red. Es una mejora con respecto al antiguo XMLHttpRequest y permite a los desarrolladores hacer solicitudes tanto a destinos de mismo origen como de origen cruzado, por lo que es una herramienta valiosa para manejar CORS.
Combinar Fetch con CORS permite a los desarrolladores hacer solicitudes de origen cruzado directamente desde sus aplicaciones web, proporcionando una forma de interactuar con otros sitios y servicios a través de sus APIs. Esto puede expandir enormemente las capacidades de una aplicación web, permitiéndole obtener datos de diversas fuentes, integrarse con otros servicios e interactuar con la web en general.
Sin embargo, al igual que con todo lo relacionado con la seguridad y la web, es importante usar estas herramientas con prudencia. CORS es una característica de seguridad diseñada para proteger a los usuarios y sus datos, por lo que es esencial entender cómo funciona y cómo usarlo correctamente. Fetch, aunque potente y flexible, es una API de bajo nivel que requiere una buena comprensión de HTTP y la política de mismo origen para ser utilizada de manera efectiva y segura.
La API Fetch y CORS son herramientas esenciales en el desarrollo web moderno. Comprender cómo funcionan juntos es clave para construir aplicaciones web sofisticadas que puedan interactuar con la web en general mientras protegen la seguridad del usuario.
Ejemplo: Fetch con CORS
fetch('<https://api.another-domain.com/data>', {
method: 'GET',
mode: 'cors', // Ensure CORS mode is set if needed
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('CORS or network error:', error));
En este ejemplo, estamos utilizando la API Fetch, una interfaz integrada en el navegador para realizar solicitudes HTTP. Está basada en promesas, lo que significa que devuelve una Promesa que se resuelve en el objeto Response que representa la respuesta a la solicitud.
Así es como funciona:
fetch('<https://api.another-domain.com/data>', {...})
: La funciónfetch()
se llama con la URL de la API a la que queremos acceder. Toma dos argumentos: la entrada y la configuración opcional (init). La entrada es la URL que estamos solicitando y la configuración es un objeto de opciones que contiene cualquier configuración personalizada que desees aplicar a la solicitud.method: 'GET'
: Esta opción indica el método de solicitud, en este caso, GET. El método GET se usa para solicitar datos de un recurso especificado.mode: 'cors'
: Este es el modo de la solicitud. Aquí se establece como 'cors', que significa Cross-Origin Resource Sharing (Intercambio de Recursos de Origen Cruzado). Este es un mecanismo que permite o bloquea los recursos solicitados en función del dominio de origen. Se necesita cuando queremos permitir solicitudes provenientes de diferentes dominios.headers: {...}
: Los encabezados de la solicitud se establecen en esta sección. El encabezado 'Content-Type' se establece en 'application/json', lo que significa que el servidor interpretará los datos enviados como un objeto JSON..then(response => response.json())
: Una vez realizada la solicitud, la API Fetch devuelve una Promesa que se resuelve en el objeto Response. El método.then()
es un método de Promesa utilizado para funciones de callback para el éxito y el fallo de las Promesas. Aquí, el objeto response se pasa a una función callback donde se convierte en formato JSON utilizando el métodojson()
..then(data => console.log(data))
: Después de la conversión a JSON, los datos se pasan a otro callback de.then()
donde se registran en la consola..catch(error => console.error('CORS or network error:', error))
: El métodocatch()
aquí se usa para capturar cualquier error que pueda ocurrir durante la operación fetch. Si ocurre un error durante la operación, se pasa a una función callback y se registra en la consola.
En resumen, este código envía una solicitud GET a la URL especificada y registra la respuesta (o cualquier error que pueda ocurrir) en la consola. El uso de promesas con los métodos .then()
y .catch()
permite manejar operaciones asíncronas, haciendo posible esperar la respuesta del servidor y manejarla una vez que esté disponible.