Quiero usar promesas (nativas) en mi aplicación de interfaz para realizar solicitudes XHR pero sin toda la tontería de un marco masivo.
Quiero que mi xhr para volver una promesa, pero esto no funciona (me da: Uncaught TypeError: Promise resolver undefined is not a function
)
function makeXHRRequest (method, url, done) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function() { return new Promise().resolve(); };
xhr.onerror = function() { return new Promise().reject(); };
xhr.send();
}
makeXHRRequest('GET', 'http://example.com')
.then(function (datums) {
console.log(datums);
});
javascript
xmlhttprequest
promise
Algunos gatitos
fuente
fuente
Respuestas:
Supongo que sabes cómo hacer una solicitud XHR nativa (puedes repasar aquí y aquí )
Dado que cualquier navegador que admita promesas nativas también admitirá
xhr.onload
, podemos omitir toda laonReadyStateChange
tontería. Retrocedamos un paso y comencemos con una función de solicitud XHR básica usando devoluciones de llamada:¡Hurra! Esto no implica nada terriblemente complicado (como encabezados personalizados o datos POST), pero es suficiente para que avancemos.
El constructor de promesas
Podemos construir una promesa así:
El constructor de la promesa toma una función a la que se le pasarán dos argumentos (llamémoslos
resolve
yreject
). Puede considerarlos como devoluciones de llamada, una para el éxito y otra para el fracaso. Los ejemplos son asombrosos, vamos a actualizarmakeRequest
con este constructor:Ahora podemos aprovechar el poder de las promesas, encadenando múltiples llamadas XHR (y
.catch
se activará un error en cualquiera de las llamadas):Podemos mejorar esto aún más, agregando parámetros POST / PUT y encabezados personalizados. Usemos un objeto de opciones en lugar de múltiples argumentos, con la firma:
makeRequest
ahora se parece a esto:Se puede encontrar un enfoque más integral en MDN .
Alternativamente, podría usar la API de recuperación ( polyfill ).
fuente
responseType
, autenticación, credenciales,timeout
... Y losparams
objetos deben admitir blobs / bufferviews eFormData
instanciasxhr.status
yxhr.statusText
en caso de error, ya que están vacíos en ese caso.resolve(xhr.response | xhr.responseText);
En la mayoría de los navegadores, la respuesta es en respuestaTexto mientras tanto.Esto podría ser tan simple como el siguiente código.
Tenga en cuenta que este código solo activará la
reject
devolución de llamada cuandoonerror
se llame ( solo errores de red ) y no cuando el código de estado HTTP indique un error. Esto también excluirá todas las demás excepciones. Manejar esos debe ser tu decisión, OMI.Además, se recomienda llamar a la
reject
devolución de llamada con una instancia deError
y no el evento en sí, pero por simplicidad, lo dejé como está.E invocarlo podría ser esto:
fuente
xhr.send(requestBody)
Para cualquiera que busque esto ahora, puede usar la función de búsqueda . Tiene bastante buen soporte .
Primero utilicé la respuesta de @ SomeKittens, pero luego descubrí
fetch
que lo hace por mí de forma inmediata :)fuente
fetch
función, pero GitHub ha publicado un polyfill .fetch
ya que aún no admite la cancelación.Creo que podemos hacer que la respuesta superior sea mucho más flexible y reutilizable al no hacer que cree el
XMLHttpRequest
objeto. El único beneficio de hacerlo es que no tenemos que escribir 2 o 3 líneas de código para hacerlo, y tiene el enorme inconveniente de quitar nuestro acceso a muchas de las funciones de la API, como configurar encabezados. También oculta las propiedades del objeto original del código que se supone que maneja la respuesta (tanto para éxitos como para errores). Por lo tanto, podemos hacer una función más flexible y más aplicable simplemente aceptando elXMLHttpRequest
objeto como entrada y pasándolo como resultado .Esta función convierte un
XMLHttpRequest
objeto arbitrario en una promesa, tratando los códigos de estado que no son 200 como un error por defecto:Esta función encaja muy naturalmente en una cadena de
Promise
s, sin sacrificar la flexibilidad de laXMLHttpRequest
API:catch
se omitió anteriormente para mantener el código de muestra más simple. Siempre debe tener uno y, por supuesto, podemos:Y deshabilitar el manejo del código de estado HTTP no requiere muchos cambios en el código:
Nuestro código de llamada es más largo, pero conceptualmente, todavía es simple entender lo que está sucediendo. Y no tenemos que reconstruir toda la API de solicitud web solo para admitir sus características.
También podemos agregar algunas funciones convenientes para ordenar nuestro código:
Entonces nuestro código se convierte en:
fuente
La respuesta de jpmc26 es bastante perfecta en mi opinión. Sin embargo, tiene algunos inconvenientes:
POST
-requests establezca el cuerpo de la solicitud.send
llamada crucial está oculta dentro de una función.Monkey parcheando el objeto xhr aborda estos problemas:
Ahora el uso es tan simple como:
Por supuesto, esto presenta un inconveniente diferente: el parcheado de monos perjudica el rendimiento. Sin embargo, esto no debería ser un problema suponiendo que el usuario está esperando principalmente el resultado del xhr, que la solicitud en sí misma toma órdenes de magnitud más largas que la configuración de la llamada y que las solicitudes xhr no se envían con frecuencia.
PD: Y, por supuesto, si se dirige a los navegadores modernos, ¡use fetch!
PPS: Se ha señalado en los comentarios que este método cambia la API estándar, lo que puede ser confuso. Para mayor claridad, se podría aplicar un método diferente al objeto xhr
sendAndGetPromise()
.fuente
send
llamada real, pero también puede confundir a los lectores que saben quesend
no tiene valor de retorno. El uso de llamadas más explícitas deja en claro que se ha invocado una lógica adicional. Mi respuesta necesita ser ajustada para manejar argumentossend
; sin embargo, probablemente sea mejor usarlofetch
ahora.