关于javascript:递归异步到promise或callback

recursive async to promises or callback

问题:
我有一个棘手的情况,我递归地遍历文件和目录,并且当文件符合特定条件时,我使用Node的readLine(异步函数)读取该文件的第一行。 读取该行并将条目推入变量(例如depTree)。 由于我的某些代码是异步的,因此无法同步获取depTree的值。

码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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但无济于事。 我将不胜感激。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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)
    })


您可以构建一个promise数组而不是一个行数组,然后使用Promise.all等待它们全部解析(或其中任何一个拒绝)。请参阅下面的***

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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函数的用法,尽管如果在上面天真地使用它们,它们会使代码比当前的更具串行性(当前并行读取许多行,这很好)。


假设:OP在读取文件方面没有问题,但是在a / sync方面。
我已使用" BUGUTILS.blocker(3)"来模拟读取的同步文件。

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

可以代替'finish(...)'或其他任何东西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
"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("\
\\t"),"\\t");
}).catch(e=>{
    console.log('Finsished with error',e);
})

console.log('after')

如果运行上面的代码将" BUGUTILS.blocker(3)"替换为同步调用,则会看到事件链。在所有异步调用完成之前,将执行" After"输出语句-但是直到所有的Promise都解决后,脚本才会完成。