fs 在使用promise包装后,执行顺序混乱
发布于 7 年前 作者 lyt308012546 5332 次浏览 来自 问答

谢谢各位,问题已解决,可以看3楼,说的很清楚。

在使用fs包一层promise后,使用async await执行,导致顺序错乱,log也不打,只会执行一次fs方法,下面是输出和代码

D:\code\mygitali\nodestudy>node index.js
开始创建文件夹

D:\code\mygitali\nodestudy>node index.js
开始删除文件夹

D:\code\mygitali\nodestudy>node index.js
开始创建文件夹

D:\code\mygitali\nodestudy>node index.js
开始删除文件夹

await-fs.js

const fs = require('fs');
const ab = ['access', 'rename', 'ftruncate', 'chown', 'lchown', 'cmod', 'fchmod', 'stat', 'lstat', 'fstat', 'link', 'symlink',
    'readlink', 'realpath', 'unlink', 'rmdir', 'readdir', 'close', 'open', 'utimes',
    'futimes', 'fsync', 'write', 'read', 'readFile', 'writeFile', 'appendFile', 'mkdir', 'mkdtemp']
// fchown fdatasync mkdtemp rename truncate
ab.forEach(name => {
    if (!fs[name])return;
    exports[name] = (...n) => {
        return new Promise((res, rej) => {
            fs[name](...n, (er, d) => {
                if (er) rej(er)
                if (d) res(d)
            })
        })
    }
})

exports.exists = (path) => {
    return new Promise((res, rej) => {
        fs.exists(path, (exists) => {
            res(exists);
        })
    });
}

index.js

const fs = require('./await-fs');

const run = async () => {
    try {
        let path = './lib';
        // 判断有没有lib文件夹
        let exists = await fs.exists(path);
        if (exists) {
            console.log('开始删除文件夹')
            // 删除文件夹
            let re1s =await fs.rmdir(path);
            console.log('删除文件夹完成',re1s)
        }

        console.log('开始创建文件夹')
        // 创建文件夹
        let res = await fs.mkdir('./lib');
        console.log('创建文件夹完成', res)

        console.log('开始创建文件')
        await fs.writeFile('./lib/1.txt', '大哥', 'utf8');
        console.log('创建文件夹完成')
    }
    catch (err) {
        console.log(err)
    }

}
run()
8 回复

单独执行以下你用promise包装的fs函数是否正确。 另外如果用bluebird的话直接用promisifyAll就好了吧,不用这样一个一个包装

问题在这个位置

return new Promise((res, rej) => {
  fs[name](...n, (er, d) => {
    if (er) rej(er)
    if (d) res(d)
  })
})

看一看fs的API文档,你会发现callback里只会有err一个参数,不是像其他函数一样在操作成功时有其他参数,也就是说永远不会有d这个参数 所以这个promise永远不会resolve,而执行到这里node的事件循环中就没有任务了,所以程序终止 (而不是因为没有resolve一直处于await状态!) 可以改成

return new Promise((res, rej) => {
  fs[name](...n, (er) => {
    if (er) rej(er)
    else res(true)
  })
})

关于更简单的方法 可以参阅 util.promisify

封装具体细节可以参考这篇文章http://welefen.com/post/how-to-convert-callback-to-promise.html

@dislido 正解,回调中只有error一个参数,是我不细心了,谢谢。

const fs = require('fs');
const ab = ['access', 'rename', 'ftruncate', 'chown', 'lchown', 'cmod', 'fchmod', 'stat', 'lstat', 'fstat', 'link', 'symlink',
    'readlink', 'realpath', 'unlink', 'rmdir', 'readdir', 'close', 'open', 'utimes',
    'futimes', 'fsync', 'write', 'read', 'readFile', 'writeFile', 'appendFile', 'mkdir', 'mkdtemp']
// fchown fdatasync mkdtemp rename truncate
ab.forEach(name => {
    if (!fs[name])return;
    exports[name] = function() {
         var params=Array.prototype.slice.call(arguments);
        return new Promise((res, rej) => {
            params.push((err, d) => {
                if (err) return rej(err);
                res(d||true);
            });
            fs[name].apply(fs,params);
        })
    }
})

exports.exists = (path) => {
    return new Promise((res, rej) => {
        fs.exists(path, (exists) => {
            res(exists);
        })
    });
}

@dislido 这里是指mkdir函数吧?

@tangdaohai 是的,还有rmdir,fs里很多函数的回调都是只有一个err的

@dislido 看了你的解释发现我之前的理解有些错误,确实是因为有些fs接口没有resolve的问题,但对于会面“事件循环中没有执行任务了,所以程序终止”这个解释有些疑问,我认为是await后面的promise没有reslove调用,导致程序运行机制没有切回宏队列,不能运行后面的语句,导致程序终止

回到顶部