8

I want to use async-await for fs.access and fs.unlink. Both the function returns an error without result callback. so the problem is if the function throws error it directly goes to catch block and continue for next iteration.

const unlink = util.promisify(fs.unlink);
const access = util.promisify(fs.access);

const deleteAssetsCtrl = async (req, res) => {
    try {
        let iteration = 0;
        for (let file of fileUrls) {
            const fileUrl = file.fileUrl
            const fileLocation = path.resolve(contentFolderPath, fileUrl);
            access(fileLocation); // step 1
            unlink(fileLocation); // step 2
            const deleteRowQuery = `DELETE FROM table WHERE fileUrl = '${fileUrl}'`;
            executeQuery(deleteRowQuery); // step 3

        if (fileUrls.length == iteration){
            res.send("true");
        } else {
            res.send('false')
        }           
    } catch (error) {
        console.log('Error =>', error);
        res.send(error);
    }
}
Error => Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

How to control flow. (steps should be in sequence)

Prashant Tapase
  • 2,132
  • 2
  • 25
  • 34
  • 1
    A tip: (some) built-in Node modules have a way of getting promise-based functions without needing `promisify()`: `const { unlink } = require('fs').promises;` (or its equivalent, `const unlink = require('fs').promisify.unlink;`) - these functions are the same as those that `promisify()` returns. – RickN Aug 28 '19 at 09:33

3 Answers3

5

You need to add the await annotation on every async function (basically, everything that returns a promise)

const unlink = util.promisify(fs.unlink);
const access = util.promisify(fs.access);

const deleteAssetsCtrl = async (req, res) => {
    try {
        let iteration = 0;
        for (let file of fileUrls) {
            const fileUrl = file.fileUrl
            const fileLocation = path.resolve(contentFolderPath, fileUrl);
            await access(fileLocation); // step 1
            await unlink(fileLocation); // step 2
            const deleteRowQuery = `DELETE FROM table WHERE fileUrl = '${fileUrl}'`;
            executeQuery(deleteRowQuery); // step 3

        if (fileUrls.length == iteration){
            res.send("true");
        } else {
            res.send('false')
        }           
    } catch (error) {
        console.log('Error =>', error);
        res.send(error);
    }
}

That means that everything that you wrap in a promise using util.promisify or any other method, creates an async function that you can await for.

If you need the value returned from the function itself, you'll only have access to the content of the returned value if you await the function since without that keyword, node will just continue with the execution and won't wait for the value to be returned or the async function to finish before continuing on.

Thatkookooguy
  • 6,669
  • 1
  • 29
  • 54
2

You have to put await before each promisified function.

Can you try this :

const unlink = util.promisify(fs.unlink);
const access = util.promisify(fs.access);

const deleteAssetsCtrl = async (req, res) => {
    try {
        let iteration = 0;
        for (let file of fileUrls) {
            const fileUrl = file.fileUrl
            const fileLocation = path.resolve(contentFolderPath, fileUrl);
            await access(fileLocation);
            await unlink(fileLocation);
            const deleteRowQuery = `DELETE FROM table WHERE fileUrl = '${fileUrl}'`;
            executeQuery(deleteRowQuery); 

        if (fileUrls.length == iteration){
            res.send("true");
        } else {
            res.send('false')
        }           
    } catch (error) {
        console.log('Error =>', error);
        res.send(error);
    }
}

Edit: you'll probably need to use await on the function executeQuery and perhaps promisify it... executeQuery is asynchronous or synchronous?

Stéphane Damon
  • 231
  • 2
  • 6
1

promisify will only convert function to promise. You should use unlink, access with await, await will wait until that promise is resolved.

Note: you may need to use await for executeQuery also because it's async operation depends on it's returning promise or not.

iteration is not incremented anywhere inside the function.

const unlink = util.promisify(fs.unlink);
const access = util.promisify(fs.access);

const deleteAssetsCtrl = async (req, res) => {
    try {
        let iteration = 0;
        for (let file of fileUrls) {
            const fileUrl = file.fileUrl
            const fileLocation = path.resolve(contentFolderPath, fileUrl);
            await access(fileLocation); // step 1
            await unlink(fileLocation); // step 2
            const deleteRowQuery = `DELETE FROM table WHERE fileUrl = '${fileUrl}'`;
            await executeQuery(deleteRowQuery); // step 3 
        } // is it closed correctly
        if (fileUrls.length == iteration){
            res.send("true");
        } else {
            res.send('false')
        }           
    } catch (error) {
        console.log('Error =>', error);
        res.send(error);
    }
}
Rahul Sharma
  • 9,534
  • 1
  • 15
  • 37
  • Above code is sample code. Iteration is incremented in actual code. I just want to confirm. step1, 2, 3 should be in sequence for each iteration. if the file doesn't exist in fs.access than it should not move for unlink. – Prashant Tapase Aug 28 '19 at 11:35
  • @PrashantTapase `const exists = await access(fileLocation);` will return true/false based on that you can perform your actions. – Rahul Sharma Aug 28 '19 at 12:36
  • It is not returning true/false. It gives undefined that is the problem to use await. – Prashant Tapase Aug 29 '19 at 05:36