递归异步到承诺或回调

时间:2018-10-04 16:52:22

标签: javascript node.js asynchronous callback promise

问题: 我有一个棘手的情况,我递归地遍历文件和目录,当文件符合特定条件时,我使用Node的readLine(异步函数)读取该文件的第一行。读取该行并将条目推入变量(例如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,并且我确定有更好的方法,我已经修改了回调和Promise,但无济于事。我将不胜感激。

3 个答案:

答案 0 :(得分:1)

您可以构建一个promise数组而不是一个行数组,然后使用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函数,尽管如果在上面天真地使用它们,它们会使代码比当前的更具串行性(当前许多行读取是并行的,这很好)。

答案 1 :(得分:1)

如果在Node.js中使用async/await关键字和Promise,则可以按以下方式解决此问题:

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

答案 2 :(得分:0)

假设:OP在读取文件方面没有问题,但是在a / sync方面。 我已经使用“ 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)”替换为同步调用,则会看到事件链。在所有异步调用完成之前执行“ After”输出语句-但是直到所有的Promise都解决之前,脚本不会完成。