1

So i'm fetching images from a remote server. then converting these images to base64 and pushing them to an empty array. the problem is the array length outside of that function doesn't get updated. i'm pretty sure it has to do something with the async/await function.

here's my code

const handleSubmit = async (listing) => { 

    const product = {files:[]};
            const getBase64StringFromDataURL = (dataURL) => dataURL.replace('data:','').replace(/^.+,/, '');
    
            for (let image of listing.images) {
                    await fetch(image)
                        .then((res) => res.blob())
                        .then((blob) => {
                            // Read the Blob as DataURL using the FileReader API
                            const reader = new FileReader();
                            reader.onloadend = () => {
                                const base64 =  getBase64StringFromDataURL(reader.result);
                                product.files.push(base64)
                                // here the product.files length is updated without a problem                                
                               console.log(product.files.length)
                            };
                            reader.readAsDataURL(blob);
                        });
               } 
        // here the length is 0 
        console.log(product.files.length)

}

please help figure this out. i'm open to a better alternative !

Sb Zakaria
  • 311
  • 6
  • 18
  • Dupe: [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) short answer: use Promise.all() –  Sep 03 '22 at 11:54

2 Answers2

2

fileReader.onload is asynchronous, I think console.log() is called before files have been read. You might want to make use of promise.all & map like this -

Promise.all([...listing.images].map(image => fetch(image)))
.then(resp => Promise.all(resp.map(file => new Promise((resolve, reject) => {
    var blob = await file.blob();
    var fileReader = new FileReader();
    fileReader.onloadend = function readFile(e) {
        const base64 =  getBase64StringFromDataURL(reader.result);
        resolve(base64);
    };
    reader.readAsDataURL(blob)
}))
.then(result => {
    // get result array here
});

Use a promise & map instead of for loop.

A G
  • 21,087
  • 11
  • 87
  • 112
-1

if you are using await, you don't need then. instead you get result directly.
reader.onloadend is asynchronous. so I suggest writing a wrapper function for it:

async function pushToArray(inputBlob, inputArray) {

 return new Promise((resolve, reject)=>{
  const reader = new FileReader();
  reader.onloadend = () => {

    /*logic...*/
    inputArray.push(base64);
    resolve();   // resolve should be here

  };
  reader.readAsDataURL(inputBlob);
  

 });
}

// now your code be like:
let res = await fetch(image);
let blob = await res.blob();  // if res.blob() return a promise
await pushToArray(blob, product.files);


console.log(product.files.length); // 1



irous
  • 401
  • 3
  • 8