0

I am trying to write a post request which downloads the HTML pages of the URLs mentioned in the route then zips them and mails them to the email mentioned in the route. The route looks like http://localhost:3000/?urls=website1,website2&email=someemail . The problems I am facing is to write promises because my code starts zipping the folder before the HTML files are downloaded and then emails no attachment. I tried different promises then, catch and async, await but no help. Any help is appreciated Thanks


const getGoogleIndexHTML = (url) => {
    return new Promise((resolve, reject) => {
        request(url, (err, res, body) => err ? reject(err) : resolve(body))
    })
}

const printAndWriteGoogleIndex = async (url) => {
    try {
        const a = url.split(".")
        let googleIndexHTML = await getGoogleIndexHTML(url)
        await fs.writeFile(directory + '/' + a[1] + '.html', googleIndexHTML, (err) =>{if(err) throw err})
        console.log('File created.')
        proccessed++;
    } catch (err) {
        console.log(err)
    }
}

const saveZip = () => {
    zipper.zip("./websites", function (error, zipped) {
        if (!error) {
            zipped.compress();
            zipped.save("./package.zip", function (error) {
                if (!error) {
                    console.log("Saved successfully !");
                }
            });
        } else {
            console.log(error)
        }
    })
}

app.post('/?', (req, res) => {
    var urls = req.query.urls
    var email = req.query.email;
    var collection_urls = urls.split(",");
    collection_urls.forEach(function (url) {
        printAndWriteGoogleIndex(url)
    });
    saveZip();
    const mailOptions = {
        from: 'dhruvdesmond@gmail.com',
        to: 'dhruvdesmond@gmail.com',
        subject: 'Websites Zip file',
        text: 'Please find zip attached below.',
        attachment: [{
            path: './package.zip'
        }]
    };
    transporter.sendMail(mailOptions, function (err) {
        if (err) {
            return res.status(500).send({
                msg: err.message
            });
        } else {
            console.log("Mail sent")
        }
    })
});

1 Answers1

1

Your issue is in this code segment, where you are triggering multiple asynchronous calls and not waiting for them to complete:

collection_urls.forEach(function (url) {
    printAndWriteGoogleIndex(url)
});

Here instead of using a forEach() to iterate over the array, you should consider using a for..of loop or a traditional for loop and await on the printAndWriteGoogleIndex call:

app.post('/?', async (req, res) => {
    var urls = req.query.urls
    var email = req.query.email;
    var collection_urls = urls.split(",");
    for(url of collection_urls){
        await printAndWriteGoogleIndex(url); //wait for the printAndWriteGoogleIndex to complete
    });
    saveZip();
    ...
}

You need to wait for the individuals files to be written and when all of them are written you can zip it at once, to do this you need to await on the implicit promise returned by the printAndWriteGoogleIndex function, a implicit promise is returned as the function is an async function.

In order for the individual awaits to complete before moving to the next one, you need to use the for..of loop and not the forEach(), you can read more about this in this excellent answer by Bergi.

Fullstack Guy
  • 16,368
  • 3
  • 29
  • 44