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:

    1. 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
    2. Ya que no estás esperando. fetch, eres console.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"
    3. Hoy en día, generalmente puede reemplazar todas las llamadas a .then con una await. 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 usando new 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 llamas Promise.allCualquier 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

  • 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ón uploadImage 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ón uploadArray, 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 del storageRef.put(blob). Esto hace que la función uploadImage termine antes de que se complete la carga del archivo.

    Para solucionarlo, debes resolver la promesa dentro del controlador then del storageRef.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