这是 node/promisify 异步函数的有效语法吗?

Is this valid syntax for node/promisify async function?

我不小心输入了 await(await stat(content... 并且成功了。不确定这是否是有效的语法,或者有更好的方法吗?我正在尝试读取所有作为目录但与我的正则表达式不匹配的文件。

const fs = require('fs')
const path = require('path')
const content = path.resolve('.') + '/docs' + '/'
const util = require('util');
const stat = util.promisify(fs.stat)
const readDir = util.promisify(fs.readdir)
const directories = 'docs/';
const exclude = new RegExp(/^(adir|\.somedir)/,'i');
let newFiles = {}

async function main(){
    const ls = await readDir(directories)
    console.log('starting....');
    let newArray = []
     for (let index = 0; index < ls.length; index++) {
               let x =  await (await stat(content + ls[index])).isDirectory()
               let file = ls[index]
               if (x && !(exclude.test(file))){newArray.push(file)}
               console.log('x is ',x);
        }   
    console.log('new filtered array: ', newArray);
}

isDirectory returns 一个 boolean,而不是一个 Promise<boolean> 所以第二个 await 是多余的,你可以只写 (await stat(content + ls[index])).isDirectory()

因为它有效,语法是有效的 - 但代码令人困惑,如果不进行一些调整可能不应该使用。这里要知道的重要一点是它对 await 不是 Promise 的东西是有效的。如果右边的表达式是一个 Promise,那么整个事情都会解析为 resolved Promise 的值;如果右侧的表达式不是 Promise,则整个表达式将解析为该值。即:

await Promise.resolve(5)

基本相同
await 5

但是虽然第二个 有效 ,但它令人困惑 - 最好只 await 是 Promises 的东西。 fs.isDirectory 不是 return Promise,所以从中删除 await 是个好主意。

对于您正在做的事情,还有一个更好的方法:改用 Promise.all,以便可以一次搜索目录中的所有项目,而不必逐个等待它们-一。如果目录中有很多项目,您当前的代码将花费很长时间 - 这不是必需的。

您还可以使用正则表达式文字代替 new RegExp 来简化正则表达式。

const exclude = /^(?:adir|\.somedir)/i;

async function main() {
    const filenames = await readDir(directories);
    const newArray = await Promise.all(
        filenames.map(async (filename) => {
            const fileStat = await stat(content + filename);
            if (fileStat.isDirectory && !(exclude.test(file))) {
                return filename;
            }
        })
    )
    const results = newArray.filter(Boolean);
    console.log('new filtered array: ', results);
}

您也可以考虑使用 fs.promises 代替 util.promisify.

const { stat, readdir } = require('fs').promises;

ls

我的建议是不要将所有鸡蛋放在一个篮子里。我们可以使用 Node 的 fs.Dirent 对象编写超快的 ls 函数,并绕过对 each 文件 -[=46 的缓慢 fs.stat 调用的需要=]

// fsext.js

import { readdir } from "fs/promises"
import { join } from "path"

async function* ls (path = ".")
{ yield { dir: path }
  for (const dirent of await readdir(path, { withFileTypes: true }))
    if (dirent.isDirectory())
      yield *ls(join(path, dirent.name))
    else
      yield { file: join(path, dirent.name) }
}

async function toArray (iter)
{ const r = []
  for await (const v of iter)
    r.push(v)
  return r
}

export { ls, toArray }
// main.js

import { ls, toArray } from "./fsext.js"

toArray(ls("./node_modules")).then(console.log, console.error)

为了测试它,让我们添加一些流行的 npm 包,以便我们有一个大的层次结构来测试我们的程序。我们将安装 lot 并计算目录和文件的数量 -

$ npm install async chalk commander debug express immutable lodash moment prop-types react react-dom request webpack

$ find ./node_modules | wc -l
5453

现在 运行 我们的程序和 time 它 -

$ time node main.js
[
  { dir: './node_modules' },
  { dir: 'node_modules/.bin' },
  { file: 'node_modules/.bin/acorn' },
  { file: 'node_modules/.bin/browserslist' },
  { file: 'node_modules/.bin/loose-envify' },
  { file: 'node_modules/.bin/mime' },
  { file: 'node_modules/.bin/sshpk-conv' },
  { file: 'node_modules/.bin/sshpk-sign' },
  { file: 'node_modules/.bin/sshpk-verify' },
  { file: 'node_modules/.bin/terser' },
  { file: 'node_modules/.bin/uuid' },
  { file: 'node_modules/.bin/webpack' },
  { file: 'node_modules/.package-lock.json' },
  { dir: 'node_modules/@types' },
  { dir: 'node_modules/@types/eslint' },
  { file: 'node_modules/@types/eslint/LICENSE' },
  { file: 'node_modules/@types/eslint/README.md' },
  { file: 'node_modules/@types/eslint/helpers.d.ts' },
  { file: 'node_modules/@types/eslint/index.d.ts' },
  { dir: 'node_modules/@types/eslint/lib' },
   ... 5433 more items
]
node main.js  0.09s user 0.02s system 116% cpu 0.099 total

目录

如果我们只想要目录,我们可以将 dirs 写成泛型 ls -

的简单特化
// fsext.js (continued)

async function* dirs (path)
{ for await (const f of ls(path))
    if (f.dir)
      yield f.dir
}
$ find ./node_modules -type d | wc -l
457

现在将其与我们的程序进行比较

// main.js

import { dirs, toArray } from "./fsext.js"

toArray(dirs("./node_modules")).then(console.log, console.error)
$ time node.main.js
[
  './node_modules',
  'node_modules/.bin',
  'node_modules/@types',
  'node_modules/@types/eslint',
  'node_modules/@types/eslint/lib',
  'node_modules/@types/eslint/lib/rules',
  'node_modules/@types/eslint/rules',
  'node_modules/@types/eslint-scope',
  'node_modules/@types/estree',
  'node_modules/@types/json-schema',
  'node_modules/@types/node',
  'node_modules/@types/node/assert',
  'node_modules/@types/node/dns',
  'node_modules/@types/node/fs',
  'node_modules/@types/node/stream',
  'node_modules/@types/node/timers',
  'node_modules/@types/node/ts3.6',
  'node_modules/@webassemblyjs',
  'node_modules/@webassemblyjs/ast',
  'node_modules/@webassemblyjs/ast/esm',
  ... 437 more items
]
node main2.js  0.09s user 0.02s system 108% cpu 0.099 total

排除

如果我们想exclude某些目录或文件,我们也可以写成通用的-

// fsext.js (continued)

async function* exclude (iter, test)
{ for await (const v of iter)
    if (Boolean(test(v)))
      continue
    else
      yield v
}
// main.js

import { dirs, exclude, toArray } from "./fsext.js"

toArray(exclude(dirs("./node_modules"), v => /@/.test(v)))
  .then(console.log, console.error)

$ time node main.js
[
  './node_modules',
  'node_modules/.bin',
  'node_modules/accepts',
  'node_modules/acorn',
  'node_modules/acorn/bin',
  'node_modules/acorn/dist',
  'node_modules/ajv',
  'node_modules/ajv/dist',
  'node_modules/ajv/lib',
  'node_modules/ajv/lib/compile',
  'node_modules/ajv/lib/dot',
  'node_modules/ajv/lib/dotjs',
  'node_modules/ajv/lib/refs',
  'node_modules/ajv/scripts',
  'node_modules/ajv-keywords',
  'node_modules/ajv-keywords/keywords',
  'node_modules/ajv-keywords/keywords/dot',
  'node_modules/ajv-keywords/keywords/dotjs',
  'node_modules/ansi-styles',
  'node_modules/array-flatten',
  ... 351 more items
]
node main.js  0.09s user 0.02s system 105% cpu 0.104 total

重组

在我们的文件系统扩展模块 fsext 中,我们编写了两个适用于 any 可迭代对象的函数,而不仅仅是 ls 或 [=36] =].我建议将它们分解成它们自己的 iter 模块。这种类型的重组有助于在整个程序中解耦问题并最大限度地重用代码 -

// iter.js

async function* empty () {}

async function* exclude (iter = empty(), test = Boolean)
{ for await (const v of iter)
    if (Boolean(test(v)))
      continue
    else
      yield v
}

async function toArray (iter = empty())
{ const r = []
  for await (const v of iter)
    r.push(v)
  return r
}

export { empty, exclude, toArray }
// fsext.js

import { readdir } from "fs/promises"
import { join } from "path"

async function* ls (path = ".")
{ yield { dir: path }
  for (const dirent of await readdir(path, { withFileTypes: true }))
    if (dirent.isDirectory())
      yield *ls(join(path, dirent.name))
    else
      yield { file: join(path, dirent.name) }
}

async function* dirs (path)
{ for await (const f of ls(path))
    if (f.dir)
      yield f.dir
}

async function* files (path)
{ for await (const f of ls(path))
    if (f.file)
      yield f.file
}

export { ls, dirs, files }
// main.js

import { dirs } from "./fsext.js"
import { exclude, toArray } from "./iter.js"

const somePath = "..."
const someTest = v => ...

toArray(exclude(dirs(somePath), someTest))
  .then(console.log, console.error)

搜索

正在查找特定文件或文件夹?继续阅读 以实施 search

gorg 和 certainPerformance 功不可没。这是一个简单的解决方案。

const { stat, readdir } = require('fs').promises;

async function main() {
    try {
        const getFiles = await readdir(directories, { withFileTypes: true })
        let foo = getFiles.filter(x=> x.isDirectory() && ! excludeDir.test(x.name))
        .map(f=>f.name);
    } catch (err) {
    console.error(err);
    }
}