Node.js循环嵌套异步回调的填坑总结

Node.js循环嵌套异步回调的填坑总结
举个栗子:
const {join, parse, resolve} = require('path')
const fs = require('fs')
/**
 * 遍历目录下所有文件
 * @param {String} filePath 要遍历的文件夹路径
 * @param {String[]} fileArr 保存文件路径的数组
 */@returns 文件数组
const getFiles = async (filePath, fileArr = []) => {
    // 定义递归调用时保存文件路径的数组
    var tFile = fileArr
    // 读取filePath下所有文件(只会读取文件名和目录名)
    const files = fs.readdirSync(filePath)
    // 遍历读取到的文件信息(其中可能包含目录)
    files.forEach(file => {
        // 根据filePath和文件名,组合成完整路径
        const fileAbsPath = join(filePath, file)
        // 判断该路径是目录还是文件
        fs.stat(fileAbsPath, (err, stats) => {
            if (!err) {
                // 如果是文件
                if (stats.isFile()) {
                    // 加入文件数组
                    tFile.push(fileAbsPath)
                } else {
                    // 否则,带着文件数组递归
                    getFiles(fileAbsPath, tFile)
                }
            }
        })
    })
    return tFile
}

上面的代码看似没有半点毛病,当我们调用的时候,返回的却是这样的东西:

const models = await getFiles(appPath)
console.log(models)
#输出结果:
PS G:\projects\node\egg-core> npm run start

> egg-core@1.0.0 start G:\projects\node\egg-core
> node app.js
[]
...
...

欸我就奇了怪了,明明逻辑没有问题啊,怎么返回一个空数组嘞

仔细检查源码后发现,fs.stat这个函数是一个异步回调函数,在nodejs中,异步IO会被放入事件栈,然后优先执行后面的代码

解决方法:
  • 使用fs模块中自带的sync方法进行替代

fs模块中每个异步的方法都有一个相对应的同步方法,使用同步方法替代异步方法即可

/**
 * 遍历目录下所有文件
 * @param {*} filePath 要遍历的文件夹路径
 * @param {*} fileArr 保存文件路径的数组
 * @returns 文件数组
 */
const getFiles = async (filePath, fileArr = []) => {
    const tFile = fileArr
    const files = fs.readdirSync(filePath)
    files.forEach(file => {
        const fileAbsPath = join(filePath, file)
        // 使用fs.statSync替代fs.stat异步回调
        const isFile = fs.statSync(fileAbsPath).isFile()
        if (isFile) tFile.push(fileAbsPath)
        else getFiles(fileAbsPath, tFile)
    })
    return tFile
}
  • 使用Promise对异步IO进行封装
const getFileStatSync = filePath => {
    return new Promise((resolve, reject) => {
        fs.stat(filePath, (err, stats) => {
            if (!err) {
                resolve(stats)
                return
            }
            reject(err)
        })
    })    
}

然后在需要调用的地方添加await

/**
 * 遍历目录下所有文件
 * @param {*} filePath 要遍历的文件夹路径
 * @param {*} fileArr 保存文件路径的数组
 * @returns 文件数组
 */
const getFiles = async (filePath, fileArr = []) => {
    const tFile = fileArr
    const files = fs.readdirSync(filePath)
    files.forEach(file => {
        const fileAbsPath = join(filePath, file)
        // 此处使用await关键词,要在函数定义的地方添加async关键字,如本函数['async (filePath, fileArr = [])']
        const isFile = await getFileStatSync(fileAbsPath).isFile()
        if (isFile) tFile.push(fileAbsPath)
        else getFiles(fileAbsPath, tFile)
    })
    return tFile
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值