網站搜尋

javascript - 在 node.js 中使用檔案系統與 async/await


我想在一些檔案系統操作中使用 async/await。通常 async/await 運作正常,因為我使用了 babel-plugin-syntax-async-functions。

但是使用這段程式碼時,我遇到了 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();

當我將程式碼重建成回呼地獄版本時,一切正常,我得到了檔案名稱。感謝你的提示。

答案#1

自 Node 11 起對異步/等待 fs 函數的原生支持

自 Node.js 11.0.0(穩定版)和 10.0.0(實驗版)起,你可以訪問已經被 promisify 的檔案系統方法。得益於 promises,你可以簡單地使用 try catch 來處理異常,而不必檢查回呼函數返回值中是否包含錯誤。

這個 API 非常乾淨且優雅!只需從 fs/promises 導入檔案系統方法,而不是直接從 fs 導入它們:

import fs from 'fs/promises'

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

listDir()

答案#2

從 node 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

答案#3

Node.js 8.0.0

原生 async/await

承諾化

從此版本開始,您可以使用來自 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()

建議

如果你不想將例外重新拋出給上層,請務必對 await 區塊使用 try..catch。

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

答案#5

你可能會產生錯誤的行為,因為 File-Api fs.readdir 並不返回 promise。它只接受回呼函數。如果你想使用 async-await 語法,你可以像這樣將函數『promisify』化:

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

答案#6

Node v14.0.0 及以上版本

你可以直接做:

import { readdir } from "fs/promises";

就像你會從 "fs" 導入一樣

查看此 PR 以獲取更多詳情:https://github.com/nodejs/node/pull/31553

答案#7

這是該問題的 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);
}