在node.js中通过async / await使用文件系统


129

我想对一些文件系统操作使用异步/等待。由于我使用,通常async / await可以正常工作babel-plugin-syntax-async-functions

但是使用此代码,我遇到了if情况,其中where names未定义:

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();

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

Answers:


139

从节点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


7
在节点v8.9.4中,收到一条SyntaxError: Unexpected token import错误消息。node8 import默认情况下是否支持令牌?
makerj

9
@makerj他正在使用新import语法。当前需要一些转堆。将确定也使用const fs = require('fs')const { promisify } = require('util')
乔什Sandlin

2
Noob问题,但是{err, names} = function语法叫什么?
卡西姆

6
@Qasim称为解构分配。
jaredkwright

1
@AlexanderZeitler可能是真的。我没看过这是否真的是正确的解构方法。在异步等待的情况下,我想您会这样做,names = await readdir('path/to/dir');并且如果块中有它的err句柄catch。无论哪种方式,语法的名称都是解构分配,这只是为了响应Qasim的问题。
jaredkwright

88

从节点11开始,对async await函数的本机支持

从Node.JS 11.0.0(稳定版)和10.0.0版(实验版)开始,您可以访问已实现协议的文件系统方法,并且可以将它们用于try catch异常处理,而不必检查回调的返回值是否包含一个错误。

该API非常干净优雅!只需使用object的.promises成员fs

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网站上的文件系统文档,此API自版本11.x起是稳定的
TheHanna19,19年

1
@DanStarns,如果您不return await履行承诺,那就不要使用catch块了……我想这有时是个很好的习惯,在返回之前等待
538ROMEO

@ 538ROMEO只是调查了这个和您的权利。感谢您指出。
DanStarns


86

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如果您不想抛出异常上限,请始终用于await块。


这很奇怪。我收到了SyntaxError:await仅在异步函数中有效...愤怒地哭泣。
Vedran Maricevic。

2
@VedranMaricevic。看评论,await必须始终在async块中:)
dimpiax

@VedranMaricevic。您需要const res = await readFile('data.json') console.log(res)在一些异步函数中调用它
-Jayraj

承诺包装fs.promises和使用它async/await令我感到困惑
oldboy

@PrimitiveNom承诺可以在传统的方式中使用thencatch等哪里异步/ AWAIT是现代行为流。
dimpiax

43

您可能会产生错误的行为,因为File-Api fs.readdir不会返回promise。它只需要一个回调。如果要使用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');

31

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,

1
@DavidP您正在使用哪个版本的节点?12岁及以上的作品效果很好
DanStarns

2
是! 完全正确-我忽略了我所使用的版本:v10.15.3-可以隐藏该消息。但是,由于问题仍然悬而未决,我认为值得一提。
DavidP

1
@DavidP我的意思是值得一提的不要误会我,但是节点12现在在LTS中,所以它不是Biggie。
DanStarns

您如何将其与例如一起使用readFile?即时消息对我来说是全新的,我要做的就是拥有一个getContent可以在整个脚本中的各个部分调用和等待的函数,但这事实证明非常令人困惑
oldboy

8

这是问题的TypeScript版本。在节点11.0之后可用:

import { promises as fs } from 'fs';

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

5

这对我有用:

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);
  }
})();

启用和声标志时,此代码可在节点7.6上工作且不包含babel node --harmony my-script.js。从节点7.7开始,您甚至不需要此标志

fsp开头包含的库只是fs(和fs-ext)的约定包装。

这些天,我真的很想知道您可以在没有babel的节点上做什么!本机async/ await使编写代码如此愉快!

更新2017-06:不推荐使用fs-promise模块。使用fs-extra,而不是用相同的API。


为此下载一个库纯属矫over过正,依赖性膨胀是社区应该强烈反对的事情,实际上应该引入一个新的npmjs,它只有具有0个依赖性的库
PirateApp

5

与自定义函数相比,建议使用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');

其他答案已过时


5

我有这个小的帮助模块,可以导出功能的承诺版本fs

const fs = require("fs");
const {promisify} = require("util")

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.