Функция генератора для перечисления файлов


Я хочу реализовать функцию, которая перебирает все файлы в каталоге. Я думаю, что функция должна

  • Возвращает обещание, потому что базовых readdir вызов любимого по readdirSync.
  • Быть генератор функций, потому что проверка statSync(...).isFile() может занять некоторое время и, возможно, мне понадобится только первые несколько файлов...

Так что моя попытка

import * as fs from 'fs';
import * as path from 'path';

function enumerateFiles(dir: string): Promise<IterableIterator<string>> {
    let result = function* (files: string[]): IterableIterator<string> {
        for (let enty of files) {
            let fullpath = path.join(dir, enty);

            if (fs.statSync(fullpath).isFile())
                yield fullpath;
        }
    };

    return new Promise<IterableIterator<string>>((resolve, reject) => {
        fs.readdir(dir, (err, files) => {
            if (err)
                reject(err);
            else
                resolve(result(files));
        });
    });
}

И использование

for (let file of await enumerateFiles(String.raw`C:\Windows\Web\Screen`))
    console.log(file);

// C:\Windows\Web\Screen\img100.jpg
// C:\Windows\Web\Screen\img101.jpg
// ...

Что вы думаете? Я действительно не уверен, что об объединении обещает с функциями генератора. Особенно в таком порядке...



Комментарии
2 ответа

Сейчас самая большая проблема с этой функцией, что я вижу, это смешение async и синхронизации fs API-интерфейсы. Это сбивает с толку для потребителя - если мне придется иметь дело с обещаниями, то, скорее всего, я буду ожидать, что вы делаете все асинхронно за кулисами.

Я думаю, что эта функция является хорошей идеей, но было бы лучше, если бы вы воспользовались асинхронный итератор предложение, которое машинопись можете transpile.

import * as fs from 'fs'
import { join } from 'path'
import { promisify } from 'util'

const statPromise = promisify(fs.stat)
const readDirPromise = promisify(fs.readdir)

const isFile = (path: string) => statPromise(path)
.then(stats => stats.isFile())

export async function * enumerateFiles (path: string): AsyncIterableIterator<string> {
const files = await readDirPromise(path)
for (const file of files) {
if (await isFile(join(path, file)) {
yield file
}
}
}

А вот пример использования:

// Async function is required.
async function main () {
for await (const file of enumerateFiles('.')) {
console.log(file)
}
}

main()

Обратите внимание, что использовать async итераторы, вам нужно добавить esnext.asynciterable к lib раздел tsconfig.json.

1
ответ дан 14 февраля 2018 в 11:02 Источник Поделиться

for (let file of await enumerateFiles(String.raw`C:\Windows\Web\Screen`))

Ожидания выглядит как дырявая абстракция. Может ваш генератор естественно ведут себя синхронно? Цитата из исследования на ES6


Случае использования 22.1.5: получение асинхронных данных

Генераторы могут получать ввод из следующей() через выход. Это означает, что вы можете проснуться генератора, когда поступят новые данные асинхронно и к генератору он чувствует, как он получает данные одновременно.


1
ответ дан 6 февраля 2018 в 07:02 Источник Поделиться