рекурсивная асинхронность с обещаниями или обратным вызовом - PullRequest
0 голосов
/ 04 октября 2018

Проблема : У меня сложная ситуация, я рекурсивно перебираю файлы и каталоги, и когда файл соответствует определенным критериям, я читаю первую строку этого файла, используя readLine (асинхронная функция) Node.Как только эта строка будет прочитана и запись будет помещена в переменную (например, depTree).Поскольку часть моего кода асинхронна, я не могу получить значение depTree синхронно.

Код :

const fs = require('fs'); 
const readline = require('readline');
const path = './mycoolpath/';
const depTree = [];

const imports = file => {
    const readLines = readline.createInterface({
        input: fs.createReadStream(file),
        crlfDelay: Infinity
    });
    // read each line, and push line to depTree if it passes my regex criteria
    readLines.on('line', (line) => {
        if (/lineMatchesMyregex/.test(line)) {
            depTree.push(line)
        }
    });
}

const recursiveSearch = path => {
    const files = fs.readdirSync(path);
    for (var i in files) {
        var file = path + '/' + files[i];
        var stats = fs.statSync(file);
        if (stats.isFile()) {
            imports(file);
        }
        else if (stats.isDirectory()) {
            recursiveSearch(file);
        }
    }

};

recursiveSearch(path);

//// embaressing setTimeout
// setTimeout(() => {
//     console.log(depTree)
// }, 1000) 

Попытки :

Я должен использовать setTimeout, и я уверен,есть лучший способ, я возился с обратными вызовами и обещаниями, но безрезультатно.Буду признателен за любую помощь / понимание.

Ответы [ 3 ]

0 голосов
/ 04 октября 2018

Если вы используете async/await ключевые слова и обещания в Node.js, вы можете решить эту проблему следующим образом:

const fs = require('fs');
const readline = require('readline');
const path = './mycoolpath/';
const depTree = [];

const imports = file => {
    return new Promise((resolve, reject) => {
        const readLines = readline.createInterface({
            input: fs.createReadStream(file),
            crlfDelay: Infinity
        });
        // read each line, and push line to depTree if it passes my regex criteria
        readLines.on('line', (line) => {
            if (/lineMatchesMyregex/.test(line)) {
                depTree.push(line)
            }
        });
        // once done reading all the lines, resolve the promise
        readLines.on('close', () => {
            resolve();
        })
    });
}

const recursiveSearch = async (path) => {
    const files = fs.readdirSync(path);
    for (var i in files) {
        var file = path + '/' + files[i];
        var stats = fs.statSync(file);
        if (stats.isFile()) {
            await imports(file);
        } else if (stats.isDirectory()) {
            await recursiveSearch(file);
        }
    }

};


//// embaressing setTimeout
setTimeout(async () => {
    await recursiveSearch(path);
    console.log(depTree)
}, 1000)

// or even better, to avoid too long or too short timeout
recursiveSearch(path)
    .then(() => {
        console.log(depTree)
    })
0 голосов
/ 04 октября 2018

Предположение: у OP нет проблем с чтением файлов, но есть аспекты синхронизации.Я использовал 'BUGUTILS.blocker (3)', чтобы имитировать чтение файла синхронизации.

results.forEach(result=>{
        console.log("\t",result);
    })

Может быть заменено на "finish (...)" или как угодно

"use strict";

const results = [];
const blockingPromise = ()=>{
    return new Promise((resolve,reject)=>{
        BUGUTILS.blocker(3);
        if(Math.random()<.5){
            return reject('Less than 50%');
        }
        return resolve('Greater than or equal to 50%');

    })
}
const recurseBlockingPromise= (count)=>{
    if(!count || count==0){
        console.log('all done')
    }else{
        recurseBlockingPromise(--count);
        //BUGUTILS.blocker(3);
        blockingPromise()
            .then(r=>{
                results.push(r)
                console.log('promised resolved',r);
            }).catch(e=>{
                results.push(e)
                console.log('promised rejected',e);
            })
    }
}
const BUGUTILS = require('./debug-utils');

console.log('Before')

let p = new Promise((resolve,reject)=>{
    recurseBlockingPromise(3);
    return resolve('All Good')
}).then(r=>{
    console.log('finished no error');
    results.forEach(result=>{
        console.log("\t",result);
    })
    //console.log("\t" ,results.join("\n\t"),"\t");
}).catch(e=>{
    console.log('Finsished with error',e);
}) 

console.log('after')

Если вы запустите приведенный выше код, заменив синхронный вызов на «BUGUTILS.blocker (3)», вы увидите цепочку событий.Оператор вывода «После» выполняется до завершения всех асинхронных вызовов, но сценарий не завершается, пока все обещания не будут разрешены.

0 голосов
/ 04 октября 2018

Вы можете создать массив обещаний, а не массив строк, а затем использовать Promise.all, чтобы дождаться их разрешения (или отклонения любого из них).См. *** ниже:

const fs = require('fs'); 
const readline = require('readline');
const path = './mycoolpath/';
const depTreePromises = []; // ***

const imports = file => {
    const readLines = readline.createInterface({
        input: fs.createReadStream(file),
        crlfDelay: Infinity
    });
    // read each line, and push line to depTree if it passes my regex criteria
    // *** Remember a promise
    depTreePromises.push(new Promise((resolve, reject) => {
        readLines.on('line', (line) => {
            if (/* can this fail? */) {
                reject(/*...*/);
            } else {
                resolve(/lineMatchesMyregex/.test(line) ? line : null);
            }
            // Side note: `destroy` the stream here? Since there's no need
            // for more lines?
        });
    }));
}

const recursiveSearch = path => {
    const files = fs.readdirSync(path);
    for (var i in files) {
        var file = path + '/' + files[i];
        var stats = fs.statSync(file);
        if (stats.isFile()) {
            imports(file);
        }
        else if (stats.isDirectory()) {
            recursiveSearch(file);
        }
    }

};

recursiveSearch(path);

// *** Wait for all, use result
Promise.all(depTreePromises)
    .then(depTree => depTree.filter(n => n !== null)) // Remove the ones that didn't match (can be just `n => n` if blank lines aren't a match for your regex
    .then(depTree => {
        console.log(depTree);
    })
    .catch(error => {
        // do something with the error
    });

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

...