为什么异步函数中的等待不适用于 fs 模块?

Why await within async function doesn't work for fs modules?

我正在尝试通过我的 js 代码读取 sample.json 文件。首先,我的程序检查指定路径中每个文件夹中的 sample.json。如果可用,它会读取 sample.json 并获取数据。但是使用的 await 没有按预期工作,只是在异步函数完成执行之前将空对象传递给调用函数。我已附上该问题的图片。

    async function getAvailableJson(filesPath) {
    let detectedJson = {};
    let folders = await fs.promises.readdir(filesPath);
    folders.forEach(async function(folder) {
        await fs.promises.access(path.join(filesPath, folder, "Sample.json")).then(async function() {
                jsonData = await fs.promises.readFile(path.join(filesPath, folder ,"Sample.json"))
                const directory = JSON.parse(jsonData)
                const hashvalue = Hash.MD5(jsonData)
                detectedJson[directory["dirName"]] = {
                                    name: directory["dirName"],
                                    version: directory["dirVersion"],
                                    hash: hashvalue
                                }
                                console.log(detectedJson);
                            }).catch(function(err) {
                                if(err.code === "ENOENT")
                                {}
                            });
                    });

    return detectedJson;
}

我不想使用任何同步功能,因为它会创建不必要的锁。我也尝试过 fs.readdir、fs.access 和 fs.readFile 函数。有人可以指出我在这里做错了什么吗,因为我是 Node.js 的新手,在此先感谢。

Sample Image

将您的 .forEach() 更改为使用 for/of 并且通常通过不混合 await.then() 来简化。

async function getAvailableJson(filesPath) {
    let detectedJson = {};
    let folders = await fs.promises.readdir(filesPath);
    let detectedJson = {};
    for (let folder of folders) {
        let file = path.join(filesPath, folder, "Sample.json");
        try {
            let jsonData = await fs.promises.readFile(file);
            const directory = JSON.parse(jsonData);
            const hashvalue = Hash.MD5(jsonData);
            detectedJson[directory["dirName"]] = {
                name: directory["dirName"],
                version: directory["dirVersion"],
                hash: hashvalue
            };
        } catch (err) {
            // silently skip any directories that don't have sample.json in them
            // otherwise, throw the error to stop further processing
            if (err.code !== "ENOENT") {
                console.log(`Error on file ${file}`, err);
                throw err;
            }
        }
        console.log(detectedJson);
    }
    return detectedJson;
}

变更摘要:

  1. .forEach()替换为for/of
  2. 删除 .then() 并仅使用 await
  3. 删除 .catch() 并仅使用 try/catch
  4. 删除对 fs.promises.access() 的调用,因为错误只能在 fs.promises.readFile()
  5. 上处理
  6. 如果错误不是 ENOENT,请添加日志记录,这样您就可以看到错误是什么以及它在哪个文件中。你几乎不想在没有日志记录的情况下默默地吃掉一个错误。尽管您可能希望跳过某些特定错误,但必须记录其他错误。重新抛出非 ENOENT 的错误,以便调用者看到它们。
  7. 将此处使用的所有变量声明并初始化为局部变量。

.forEach() 不是 promise-aware,所以在里面使用 await 根本不会暂停外部函数。相反,使用 for/of 循环,它不会创建额外的函数作用域,并允许 await 暂停父函数。

此外,我认为 .forEach() 如今已经过时了。它不是承诺意识的。 for/of 是一种更高效、更通用的迭代方式。而且,不再需要使用 .forEach() 回调创建新的函数作用域,因为我们有 letconst 的块作用域变量。我不再使用它了。

此外,我看不出您为什么要使用 fs.promises.access() 进行预检。这只会造成竞争条件,您也可以只处理从 fs.promises.readFile() 得到的任何错误,因为在没有竞争条件的情况下,它会做同样的事情。


另请参阅关于类似问题的