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

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

但在这段代码中,我遇到了if情况,其中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();

当我将代码重建为回调地狱版本时,一切都OK,我得到了文件名。

245643 次浏览

你可能会产生错误的行为,因为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');

以下是对我有效的方法:

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

当启用和谐的旗帜时,此代码在没有babel的节点7.6中工作:从节点7.7开始,你甚至不需要这面旗帜!

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

这些天你可以在没有babel的节点上做什么,我真的很兴奋!本机async/await使编写代码成为一种乐趣!

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

从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

node . js 8.0.0

本机异步/等待

Promisify

从这个版本开始,你可以使用来自跑龙套库的原生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作为等待块。

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

其他答案都过时了

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

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


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

从Node 11开始原生支持async/await fs函数

自从Node.JS 11.0.0(稳定)和10.0.0版本(实验)以来,你可以访问已经承诺的文件系统方法,你可以使用try catch异常处理来使用它们,而不是检查回调的返回值是否包含错误。

API非常干净和优雅!只需使用fs对象的.promises成员:

import fs from 'fs';


async function listDir() {
try {
return await fs.promises.readdir('path/to/dir');
} catch (err) {
console.error('Error occurred while reading directory!', err);
}
}


listDir();

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

这是这个问题的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);
}

节点v14.0.0及以上版本

你可以这样做:

import { readdir } from "fs/promises";

就像你从"fs"导入一样

有关更多详细信息,请参阅此PR: https://github.com/nodejs/node/pull/31553

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

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