【问题标题】:Using filesystem in node.js with async / await在 node.js 中使用带有 async / await 的文件系统
【发布时间】:2017-03-28 09:26:10
【问题描述】:

我想在一些文件系统操作中使用 async/await。通常 async/await 工作正常,因为我使用babel-plugin-syntax-async-functions

但是使用这段代码,我遇到了names 未定义的 if 情况:

import fs from 'fs';

async function myF() {
  let names;
  try {
    names = await fs.readdir('path/to/dir');
  } catch (e) {
    console.log('e', e);
  }
  if (names === undefined) {
    console.log('undefined');
  } else {
    console.log('First Name', names[0]);
  }
}

myF();

当我将代码重建为回调地狱版本时,一切正常,我得到了文件名。 感谢您的提示。

【问题讨论】:

    标签: javascript node.js async-await fs


    【解决方案1】:

    自 Node 11 起对 async await fs 函数的原生支持

    从 Node.JS 11.0.0(稳定版)和 10.0.0 版(实验版)开始,您可以访问已经承诺的文件系统方法,并且可以将它们与 try catch 异常处理一起使用,而不是检查如果回调的返回值包含错误。

    API 非常简洁优雅!只需使用fs 对象的.promises 成员:

    import fs from 'fs';
    const fsPromises = fs.promises;
    
    async function listDir() {
      try {
        return fsPromises.readdir('path/to/dir');
      } catch (err) {
        console.error('Error occured while reading directory!', err);
      }
    }
    
    listDir();
    

    【讨论】:

    • 根据 Node.js 网站上的 File System documentation,此 API 自 11.x 版起稳定
    • @DanStarns 如果你不 return await 你的承诺,catch 块是没有用的......我认为有时在返回之前等待是一个好习惯
    • @538ROMEO 刚刚调查了这个和你的权利。感谢您指出。
    • 这些替代方法的文档:nodejs.org/api/fs.html#fs_fs_promises_api
    • 为什么不用await 放在fsPromises.readdir(...) 前面?我认为async/await 必须始终一起使用。在这种情况下,await 似乎会导致问题。
    【解决方案2】:

    从节点 8.0.0 开始,你可以使用这个:

    const fs = require('fs');
    const util = require('util');
    
    const readdir = util.promisify(fs.readdir);
    
    async function myF() {
      let names;
      try {
        names = await readdir('path/to/dir');
      } catch (err) {
        console.log(err);
      }
      if (names === undefined) {
        console.log('undefined');
      } else {
        console.log('First Name', names[0]);
      }
    }
    
    myF();
    

    https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original

    【讨论】:

    • 在节点 v8.9.4 中,收到 SyntaxError: Unexpected token import 错误消息。 node8是否默认支持import token?
    • @makerj 他正在使用新的import 语法。它目前需要一些转译。也可以使用const fs = require('fs')const { promisify } = require('util')
    • 菜鸟问题,但是{err, names} = function 的语法叫什么?
    • @Qasim 称为解构赋值。
    • @AlexanderZeitler 这可能是真的。我还没有查看这是否实际上是对解构的正确使用。在异步等待的情况下,我认为您只需执行 names = await readdir('path/to/dir'); 并且如果有 errcatch 块中处理它。无论哪种方式,语法的名称都是解构赋值,这只是为了回应 Qasim 的问题。
    【解决方案3】:

    Node.js 8.0.0

    本机异步/等待

    承诺

    从这个版本开始,您可以使用 util 库中的原生 Node.js 函数。

    const fs = require('fs')
    const { promisify } = require('util')
    
    const readFileAsync = promisify(fs.readFile)
    const writeFileAsync = promisify(fs.writeFile)
    
    const run = async () => {
      const res = await readFileAsync('./data.json')
      console.log(res)
    }
    
    run()
    
    

    承诺包装

    const fs = require('fs')
    
    const readFile = (path, opts = 'utf8') =>
      new Promise((resolve, reject) => {
        fs.readFile(path, opts, (err, data) => {
          if (err) reject(err)
          else resolve(data)
        })
      })
    
    const writeFile = (path, data, opts = 'utf8') =>
      new Promise((resolve, reject) => {
        fs.writeFile(path, data, opts, (err) => {
          if (err) reject(err)
          else resolve()
        })
      })
    
    module.exports = {
      readFile,
      writeFile
    }
    
    ...
    
    
    // in some file, with imported functions above
    // in async block
    const run = async () => {
      const res = await readFile('./data.json')
      console.log(res)
    }
    
    run()
    

    建议

    如果您不想重新抛出异常,请始终将 try..catch 用于等待块。

    【讨论】:

    • 这很奇怪。我收到 SyntaxError: await is only valid in async function... 愤怒地哭泣。
    • @VedranMaricevic。看看 cmets,await 必须始终在 async 块中 :)
    • @VedranMaricevic。你需要在一些异步函数中调用const res = await readFile('data.json') console.log(res)
    • promise 包装 fs.promises 并与 async/await 一起使用让我很困惑
    • @PrimitiveNom Promise 可以在thencatch 等中以传统方式使用。async/await 是现代行为流。
    【解决方案4】:

    v10.0 开始,您可以使用fs.Promises

    使用readdir 的示例

    const { promises: fs } = require("fs");
    
    async function myF() {
        let names;
        try {
            names = await fs.readdir("path/to/dir");
        } catch (e) {
            console.log("e", e);
        }
        if (names === undefined) {
            console.log("undefined");
        } else {
            console.log("First Name", names[0]);
        }
    }
    
    myF();
    

    使用readFile 的示例

    const { promises: fs } = require("fs");
    
    async function getContent(filePath, encoding = "utf-8") {
        if (!filePath) {
            throw new Error("filePath required");
        }
    
        return fs.readFile(filePath, { encoding });
    }
    
    (async () => {
        const content = await getContent("./package.json");
    
        console.log(content);
    })();
    

    【讨论】:

    • 效果很好,但重要的是要注意有关ExperimentalWarning: The fs.promises API is experimental 警告的未解决问题:github.com/pnpm/pnpm/issues/1178
    • @DavidP 您使用的是什么版本的节点? 12及以上工作正常
    • 是的!绝对正确 - 我忽略了我正在使用的版本:v10.15.3 - 可以隐藏消息。但是,由于问题仍然存在,我认为值得一提。
    • @DavidP 我的意思是值得一提,不要误会我的意思,但节点 12 现在在 LTS 中,所以它不是 Biggie。
    • 在 TypeScript(和现代 JavaScript?)中,您可以编写 import { promises as fs } from "fs";
    【解决方案5】:

    您可能会产生错误的行为,因为 File-Api fs.readdir 不返回承诺。它只需要一个回调。如果你想使用 async-await 语法,你可以像这样“承诺”这个函数:

    function readdirAsync(path) {
      return new Promise(function (resolve, reject) {
        fs.readdir(path, function (error, result) {
          if (error) {
            reject(error);
          } else {
            resolve(result);
          }
        });
      });
    }
    

    改为调用它:

    names = await readdirAsync('path/to/dir');
    

    【讨论】:

    • 我收到了这样的奇怪响应... Buffer(18524) [60, 115, 99, 114, 105, 112, 116, 32, 116, 110, 116, 45, 108 , 105, 98, 62, 13, 10, 32, 32, 32, 32, 47, 42, 42, 13, 10, 32, 32, 32, 32, 32, 42, 32, 67, 111, 112, 121 , 114, 105, 103, 104, 116, 32, 63, 32, 50, 48, 50, 48, 32, 68, 101, 115, 105, 103, 110, 32, 65 ...]
    • 使用Buffer.toString方法。
    【解决方案6】:

    这是问题的 TypeScript 版本。在Node 11.0之后可以使用:

    import { promises as fs } from 'fs';
    
    async function loadMonoCounter() {
        const data = await fs.readFile('monolitic.txt', 'binary');
        return Buffer.from(data);
    }
    

    【讨论】:

      【解决方案7】:

      节点 v14.0.0 及更高版本

      你可以这样做:

      import { readdir } from "fs/promises";
      

      就像你从"fs"导入一样

      查看此 PR 了解更多详情:https://github.com/nodejs/node/pull/31553

      【讨论】:

        【解决方案8】:

        我有这个小帮助模块,可以导出 promisified 版本的 fs 函数

        const fs = require("fs");
        const {promisify} = require("util")
        
        module.exports = {
          readdir: promisify(fs.readdir),
          readFile: promisify(fs.readFile),
          writeFile: promisify(fs.writeFile)
          // etc...
        };
        

        【讨论】:

          【解决方案9】:

          这对我有用:

          const fsp = require('fs-promise');
          
          (async () => {
            try {
              const names = await fsp.readdir('path/to/dir');
              console.log(names[0]);
            } catch (e) {
              console.log('error: ', e);
            }
          })();
          

          当启用harmony flag 时,此代码在没有 babel 的节点 7.6 中工作:node --harmony my-script.js。从节点 7.7 开始,you don't even need this flag!

          开头包含的fsp 库只是fs(和fs-ext)的承诺包装器。

          我真的很高兴这些天你可以在没有 babel 的情况下在 node 中做什么!原生async/await 让写代码成为一种享受!

          2017-06 更新: fs-promise 模块已弃用。使用 fs-extra 代替相同的 API。

          【讨论】:

          • 为此下载一个库纯粹是矫枉过正,依赖膨胀是社区应该强烈反对的事情,实际上应该制作一个新的 npmjs,它只有具有 0 个依赖的库
          【解决方案10】:

          与自定义函数相比,建议使用 npm 包,例如 https://github.com/davetemplin/async-file。例如:

          import * as fs from 'async-file';
          
          await fs.rename('/tmp/hello', '/tmp/world');
          await fs.appendFile('message.txt', 'data to append');
          await fs.access('/etc/passd', fs.constants.R_OK | fs.constants.W_OK);
          
          var stats = await fs.stat('/tmp/hello', '/tmp/world');
          

          其他答案已过时

          【讨论】:

            【解决方案11】:

            您可以使用简单轻量级的模块https://github.com/nacholibre/nwc-l,它同时支持异步和同步方法。

            注意:这个模块是我创建的。

            【讨论】:

              猜你喜欢
              • 2019-05-30
              • 2019-01-29
              • 1970-01-01
              • 2018-01-28
              • 2019-04-21
              • 2018-04-12
              • 2018-03-09
              相关资源
              最近更新 更多