1

In a VSCode extension I have a function that removes certain directories from the current workspace. But the function does not wait for the directories to be removed.

The accepted answer for this Stackoverflow question makes sense, but still I can't figure out how to make my function work correctly. Where do I put await and return?

async function cleanUpWorkspace(workspace: any) {
    const fs = require('fs');
    const rimraf = require("rimraf");
    var targetFolders = ['dirA', 'dirB', 'dirC', 'dirD'];

    try {
        await targetFolders.forEach(function (value) { //'await' has no effect on the type of this expression.
            let targetPath = workspace + "\\\\" + value;
            if (fs.existsSync(targetPath)) {
                fs.promises.access(targetPath);
                rimraf(targetPath, function (err: any) {
                    if (err) {
                        console.log(err);
                    } else {
                        console.log("Directory: " + value + " was deleted");
                    }
                });
            } else {
                console.log(value + " doesn't exist.");
            }
        });
        return Promise.resolve();
    } catch (error) {
        console.log(error);
    }
}

async function prepare() {
    await cleanUpWorkspace(workspace);
}
  • Typescript 4.3.5
  • node.js 14.17.0
oivron
  • 75
  • 4

1 Answers1

3
  • You can only use await inside an async function or an async arrow function (async () => { /* function contents */ })
  • You should only use await on functions that return a Promise
  • All async functions, regardless of whether or not they return a value, return a Promise. If it doesn't return a value inside, the return type is Promise<void> (similar to how regular functions implicitly return void if they don't have a return value)

You are trying to do async operations (i.e call functions that return promises) inside the function passed to forEach. Ideally, we would await them. To use await, we need to make the function passed to forEach an async function. However, forEach does not internally await promises. This means that uncaught exceptions inside your forEach function do not make it up to the try/catch block.

To avoid this, we can use a for..of loop.

Because async functions automatically return a Promise, there's not really a reason you need to return Promise.resolve()

  try {
      for (const value of targetFolders) {
        let targetPath = workspace + "\\\\" + value;
        if (fs.existsSync(targetPath)) {
            await fs.promises.access(targetPath);
            rimraf(targetPath, function (err: any) {
                if (err) {
                    console.log(err);
                } else {
                    console.log("Directory: " + value + " was deleted");
                }
            });
        } else {
            console.log(value + " doesn't exist.");
        }
      }
  } catch (error) {
      console.log(error);
  }

Now, we can await the fs.promises.access call.


Another thing we can do is promisify rimraf. There is a function in the Node standard library -- util.promisify -- that converts functions taking callbacks into functions returning promises. So, if we have

const util = require("util")
const rimrafPromise = util.promisify(rimraf)

then we can do

  try {
      for (const value of targetFolders) {
        let targetPath = workspace + "\\\\" + value;
        if (fs.existsSync(targetPath)) {
            await fs.promises.access(targetPath);
            await rimrafPromise(targetPath);
            console.log("Directory: " + value + " was deleted");
        } else {
            console.log(value + " doesn't exist.");
        }
      }
  } catch (error) {
      console.log(error);
  }
Ritik Mishra
  • 658
  • 4
  • 15