Async/await and promise all not working as expected
Soy bastante nuevo en javascript y estoy teniendo problemas con async/await y promesas. Estoy tratando de subir una serie de imágenes para bombear de forma asincrónica para que las URL de nuevo para el próximo paso. Lo siguiente es la función de carga.
const uploadImage = async (file) => {
console.log("starting...");
const storageRef = firebase.storage().ref("forum/" + file[1]);
return new Promise((resolve, reject) => {
fetch(file[0])
.then(async (res) => {
console.log("res to blob");
return await res.blob();
})
.then(async (blob) => {
blob.name = file[1];
console.log("Starting to put file...", blob);
await storageRef.put(blob).then(async () => {
const url = await storageRef.getDownloadURL();
urlArray.push(url);
console.log(url);
!url ? resolve("got url!") : reject("it broke");
});
});
console.log("done!");
});
};
const uploadArray = async (imageArray) => {
return await Promise.all(
imageArray.map((image) => uploadImage(image))
);
};
uploadArray(project.images).then((urls) => {
urls.forEach((element) => {
console.log(element);
});
});
Esta es la consola cada vez que subeArray() se llama.
starting...
done!
starting...
done!
res to blob
Starting to put file... Blob {name: "IMG_3851.JPG", size: 1417851, type: "image/jpeg"}
Starting to put file... Blob {name: "IMG_3852.JPG", size: 2391056, type: "image/png"}
https://firebasestorage.googleapis.com/... (Link to firebase)
Uncaught (in promise) it broke```
https://firebasestorage.googleapis.com/... (Link to firebase)
Pero quiero que la consola inicie las declaraciones en este orden.
starting
res to blob
Starting to put file... Blob {name: "IMG_3851.JPG", size: 1417851, type: "image/jpeg"}
https://firebasestorage.googleapis.com/... (Link to firebase)
done!
starting
res to blob
Starting to put file... Blob {name: "IMG_3852.JPG", size: 2391056, type: "image/png"}
https://firebasestorage.googleapis.com/... (Link to firebase)
done!
Pregunta hecha hace 3 años, 4 meses, 29 días - Por codecanvas
4 Respuestas:
-
La forma en que manejas las promesas es un poco extraña, y tiene
new Promise
constructor anti-pattern. Reescribió las partes desordenadas aquí, y sospecho que esto resolverá su problema de pedido:const uploadImage = async (file) => { console.log("starting..."); const storageRef = firebase.storage().ref("forum/" + file[1]); const res = await fetch(file[0]); const blob = await res.blob(); blob.name = file[1]; console.log("Starting to put file...", blob); await storageRef.put(blob); const url = await storageRef.getDownloadURL(); urlArray.push(url); console.log(url); if (url) { return "got url"; } throw "it broke"; }
El snippet sobre todo hace lo que escribiste, pero todo lo innecesario entonces es eliminado. Debería ser mucho más claro. Es muy bien. vale la pena enteramente aprender promesas y async/await antes de proceder. El código que compartiste siente muy juicio y error.
Respondida el Dec 17, 2020 a las 01:37 - por codecrafty
Votos positivos: 0 | Votos negativos: 0 -
Un par de tidbits sobre JS y async/await:
- Técnicamente, las funciones sólo deben ser marcadas como asinc si utiliza
await
dentro de él - de lo contrario usted podría conseguir comportamiento indefinido cuando los errores ocurren - Ya que no estás esperando.
fetch
, eresconsole.log("done!");
debe ir después de la línea donde usted resuelve/rechazar. No es así que se ejecuta inmediatamente, es por eso que tu verlos impresos justo después"starting"
- Hoy en día, generalmente puede reemplazar todas las llamadas a
.then
con unaawait
. Evitar el "infierno de retroceso" es muy importante en la JS cuando se trata de la claridad de código. A veces no es posible moverse usandonew Promise()
, pero si se puede evitar, debe ser
Como la respuesta de @Evert, la eliminación de callbacks y el uso de esperas adecuadas probablemente solucionará sus problemas de pedido.
También sería una buena idea añadir una captura de prueba alrededor de todas las llamadas de API para que no consiga una excepción de promesa sin manejar. Se puede añadir a la
uploadImage
método o a dónde llamasPromise.all
Cualquier cosa funciona mejor para su caso de uso.const uploadImage = async (file) => { try { console.log("starting..."); const storageRef = firebase.storage().ref("forum/" + file[1]); const res = await fetch(file[0]); console.log("res to blob"); const blob = await res.blob(); blob.name = file[1]; console.log("Starting to put file...", blob); await storageRef.put(blob); const url = await storageRef.getDownloadURL(); urlArray.push(url); console.log(url); if (!url) throw new Error("it broke"); console.log("done!"); } catch (err) { console.error(err); } };
Respondida el Dec 17, 2020 a las 01:46 - por binarybrainiac
Votos positivos: 0 | Votos negativos: 0 - Técnicamente, las funciones sólo deben ser marcadas como asinc si utiliza
-
El problema que estás experimentando se debe a que la promesa que estás creando dentro de la función
uploadImage
no está siendo manejada correctamente. La funciónuploadImage
debería devolver una promesa y no crear una nueva promesa dentro de ella.Aquí te dejo una versión corregida de tu código:
javascript const uploadImage = async (file) => { console.log("starting..."); const storageRef = firebase.storage().ref("forum/" + file[1]); try { const res = await fetch(file[0]); console.log("res to blob"); const blob = await res.blob(); blob.name = file[1]; console.log("Starting to put file...", blob); await storageRef.put(blob); const url = await storageRef.getDownloadURL(); console.log(url); return url; } catch (error) { console.error(error); return null; } }; const uploadArray = async (imageArray) => { const urls = await Promise.all( imageArray.map((image) => uploadImage(image)) ); return urls.filter(url => url !== null); }; uploadArray(project.images).then((urls) => { urls.forEach((element) => { console.log(element); }); });
Con estos cambios, la función
uploadImage
ahora devuelve directamente la promesa que resuelve con la URL de descarga de la imagen subida o se rechaza si hay un error. Luego, en la funciónuploadArray
, filtramos las URLs para eliminar aquellas que sean nulas (indicando errores) antes de devolver el array de URLs.Con estas modificaciones, el orden en que aparecen las líneas en la consola debe ser como esperas. Espero que esto resuelva tu problema. ¡Déjame saber si tienes alguna otra pregunta!
Respondida el Dec 18, 2020 a las 01:38 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
El problema con tu código es que estás resolviendo la promesa dentro del
then
bloque delstorageRef.put(blob)
. Esto hace que la funciónuploadImage
termine antes de que se complete la carga del archivo.Para solucionarlo, debes resolver la promesa dentro del controlador
then
delstorageRef.getDownloadURL()
. Aquí tienes el código actualizado:javascript const uploadImage = async (file) => { console.log("starting..."); const storageRef = firebase.storage().ref("forum/" + file[1]); return new Promise((resolve, reject) => { fetch(file[0]) .then(async (res) => { console.log("res to blob"); return await res.blob(); }) .then(async (blob) => { blob.name = file[1]; console.log("Starting to put file...", blob); await storageRef.put(blob); }) .then(async () => { const url = await storageRef.getDownloadURL(); urlArray.push(url); console.log(url); !url ? resolve("got url!") : reject("it broke"); }); console.log("done!"); }); };
Ahora, la función
uploadImage
no se resolverá hasta que se complete la carga del archivo y se obtenga la URL de descarga. Esto debería solucionar el problema que estás experimentando.Respondida el Dec 18, 2020 a las 01:48 - por Gemini
Votos positivos: 0 | Votos negativos: 0