Introducción a fetch()

La API de fetch() está llegando al objeto window y busca reemplazar las XHR.

XMLHttpRequest demasiado largo

fetch() te permite realizar solicitudes de red similares a XMLHttpRequest (XHR). La diferencia principal es que la API de Fetch usa promesas, lo que permite una API más simple y limpia, lo que evita el infierno de las devoluciones de llamada y la necesidad de recordar la API compleja de XMLHttpRequest.

Navegadores compatibles

  • 42
  • 14
  • 39
  • 10.1

Origen

La API de Fetch está disponible en el alcance global Service Worker desde Chrome 40, pero estará habilitada en el alcance de la ventana en Chrome 42. También hay polyfill de GitHub que puedes obtener hoy mismo.

Si nunca usaste promesas, consulta Introducción a las promesas de JavaScript.

Solicitud de recuperación básica

Comencemos comparando un ejemplo simple implementado con un XMLHttpRequest y, luego, con fetch. Solo queremos solicitar una URL, obtener una respuesta y analizarla como JSON.

XMLHttpRequest

Un XMLHttpRequest necesitaría que se configuren dos objetos de escucha para controlar los casos de éxito y error, y una llamada a open() y send(). Ejemplo de la documentación de MDN.

function reqListener() {
    var data = JSON.parse(this.responseText);
    console.log(data);
}

function reqError(err) {
    console.log('Fetch Error :-S', err);
}

var oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.onerror = reqError;
oReq.open('get', './api/some.json', true);
oReq.send();

Recuperar

Nuestra solicitud de recuperación se ve de la siguiente manera:

fetch('./api/some.json')
    .then(
    function(response) {
        if (response.status !== 200) {
        console.log('Looks like there was a problem. Status Code: ' +
            response.status);
        return;
        }

        // Examine the text in the response
        response.json().then(function(data) {
        console.log(data);
        });
    }
    )
    .catch(function(err) {
    console.log('Fetch Error :-S', err);
    });

Comenzamos por verificar que el estado de la respuesta sea 200 antes de analizarla como JSON.

La respuesta de una solicitud fetch() es un objeto Stream, lo que significa que cuando llamamos al método json(), se muestra una promesa, ya que la lectura de la transmisión se realizará de forma asíncrona.

Metadatos de las respuestas

En el ejemplo anterior, analizamos el estado del objeto Response y cómo analizar la respuesta como JSON. A continuación, se muestran otros metadatos a los que podríamos querer acceder, como los encabezados.

fetch('users.json').then(function(response) {
    console.log(response.headers.get('Content-Type'));
    console.log(response.headers.get('Date'));

    console.log(response.status);
    console.log(response.statusText);
    console.log(response.type);
    console.log(response.url);
});

Tipos de respuesta

Cuando realizamos una solicitud de recuperación, la respuesta recibirá un response.type de "basic", "cors" o "opaque". Estas types indican de dónde proviene el recurso y se pueden usar para informar cómo debes tratar el objeto de respuesta.

Cuando se realiza una solicitud para un recurso en el mismo origen, la respuesta tendrá un tipo basic y no hay restricciones sobre lo que puedes ver en ella.

Si se realiza una solicitud para un recurso en otro origen que muestra los encabezados de CORs, el tipo es cors. Las respuestas cors y basic son casi idénticas, excepto que una respuesta cors restringe los encabezados que puedes ver a Cache-Control, Content-Language, Content-Type, Expires, Last-Modified y Pragma.

Una respuesta opaque es para una solicitud realizada a un recurso en un origen diferente que no muestra encabezados de CORS. Con una respuesta opaca, no podremos leer los datos que se muestren ni ver el estado de la solicitud, lo que significa que no podemos verificar si la solicitud se realizó de manera correcta o no.

Puedes definir un modo para una solicitud de recuperación de forma que solo se resuelvan ciertas solicitudes. Los modos que puedes configurar son los siguientes:

  • same-origin solo se realiza de forma correcta para las solicitudes de elementos del mismo origen; se rechazarán todas las demás solicitudes.
  • cors permitirá solicitudes de elementos en el mismo origen y en otros orígenes que muestren los encabezados de COR adecuados.
  • cors-with-forced-preflight siempre realizará una comprobación previa antes de realizar la solicitud real.
  • no-cors está diseñado para realizar solicitudes a otros orígenes que no tienen encabezados de CORS y dan como resultado una respuesta opaque, pero como se indicó, esto no es posible en este momento en el alcance global de la ventana.

Para definir el modo, agrega un objeto de opciones como segundo parámetro en la solicitud fetch y define el modo en ese objeto:

fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
    .then(function(response) {
    return response.text();
    })
    .then(function(text) {
    console.log('Request successful', text);
    })
    .catch(function(error) {
    log('Request failed', error)
    });

Cómo encadenar promesas

Una de las grandes características de las promesas es la capacidad de encadenarlas. En el caso de la recuperación, esto te permite compartir la lógica entre las solicitudes de recuperación.

Si trabajas con una API de JSON, deberás verificar el estado y analizar el JSON de cada respuesta. Para simplificar tu código, define el estado y el análisis de JSON en funciones separadas que muestran promesas, lo que te libera de la necesidad de preocuparte por manejar los datos finales y el caso de error.

function status(response) {
    if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
    } else {
    return Promise.reject(new Error(response.statusText))
    }
}

function json(response) {
    return response.json()
}

fetch('users.json')
    .then(status)
    .then(json)
    .then(function(data) {
    console.log('Request succeeded with JSON response', data);
    }).catch(function(error) {
    console.log('Request failed', error);
    });

Se define la función status, que verifica response.status y muestra el resultado de Promise.resolve() o Promise.reject(), que muestran una promesa resuelta o rechazada. Este es el primer método que se llama en nuestra cadena fetch(). Si se resuelve, llamamos a nuestro método json() que vuelve a mostrar una promesa de la llamada a response.json(). Luego, tenemos un objeto del JSON analizado. Si el análisis falla, se rechaza la promesa y se ejecuta la sentencia catch.

Lo mejor de esto es que puedes compartir la lógica en todas tus solicitudes de recuperación, lo que facilita el mantenimiento, la lectura y la prueba del código.

Solicitud POST

Es común que las aplicaciones web quieran llamar a una API con un método POST y proporcionar algunos parámetros en el cuerpo de la solicitud.

Para ello, podemos configurar los parámetros method y body en las opciones fetch().

fetch(url, {
    method: 'post',
    headers: {
        "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: 'foo=bar&lorem=ipsum'
    })
    .then(json)
    .then(function (data) {
    console.log('Request succeeded with JSON response', data);
    })
    .catch(function (error) {
    console.log('Request failed', error);
    });

Envía credenciales con una solicitud de recuperación

Si deseas realizar una solicitud de recuperación con credenciales como cookies, debes establecer el credentials de la solicitud en "include".

fetch(url, {
    credentials: 'include'
})

Preguntas frecuentes

¿Cómo cancelo una solicitud fetch()?

Por el momento, no hay forma de cancelar una recuperación, pero esto se analiza en GitHub. H/T (@jaffathecake) para este vínculo.

¿Hay un polyfill?

GitHub tiene un polyfill para recuperar. H/T @Nexii por señalar esto.

¿Por qué "no-cors" se admite en los service workers, pero no en la ventana?

Esto se debe a un problema de seguridad. Puedes obtener más información aquí.