【问题标题】:Converting a build using callback hell to promise hell使用回调地狱将构建转换为承诺地狱
【发布时间】:2016-08-30 13:50:32
【问题描述】:

我目前正在将带有一些回调地狱的构建过程转换为承诺地狱(地狱很可能是因为我对承诺不熟悉,而且我对 Bluebird 缺乏经验)。我正在努力通过 .all 方法,但出现文件已经存在的错误。也许其中的文件副本一开始就试图在rimraf 之前发生?

const Promise    = require('bluebird');
const rcopyAsync = Promise.promisify(require('recursive-copy'));
const readAsync  = Promise.promisify(require('recursive-readdir'));
const rmrfAsync  = Promise.promisify(require('rimraf'));
const globAsync  = Promise.promisify(require('glob'));

rmrfAsync('{build,dist}')
    .then(() => {
        return readAsync('src');
    })
    .then((files) => {
        if (!files.length) {
            return Promise.reject(new Error('No source to compile.'));
        }

        return Promise.resolve(true);
    })
    .all([
        rcopyAsync(`${__dirname}/scripting`, 'build'),
        rcopyAsync(`${__dirname}/compiler/${process.platform}`, 'build'),
        rcopyAsync('src/scripting', 'build')
    ])
    .then(() => {
        return globAsync('*.sma', { cwd: 'build' });
    })
    .then((files) => {
        console.log(files);
    })
    .catch(err => {
        throw err;
    });

对于任何感兴趣的人,回调地狱的工作部分如下:

...

rmrf('{build,dist}', err => {
    if (err) throw err;

    read('src', (err, files) => {
        if (err) throw err;
        if (!files.length) return;

        rcopy(`${__dirname}/scripting`, 'build', err => {
            if (err) throw err;

            rcopy(`${__dirname}/compiler/${process.platform}`, 'build', err => {
                if (err) throw err;

                rcopy('src/scripting', 'build', err => {
                    if (err) throw err;

                    glob('*.sma', { cwd: 'build' }, (err, files) => {
                        if (err) throw err;

                        console.log(files);
                    });
                });
            });
        });
    });
});

【问题讨论】:

    标签: javascript node.js promise ecmascript-6 bluebird


    【解决方案1】:

    你被 Promise 的工作方式欺骗了。当您第一次执行 javascript 时,链的所有部分都已构建。因为,在构建您的链时,您调用rcopyAsync,rcopyAsync 将立即开始。如果您希望稍后发生某些事情,则需要将其包装在 .then 中。

    rmrfAsync('{build,dist}')
    .then(() => {
        return readAsync('src');
    })
    .then((files) => {
        if (!files.length) {
            /* Could even be:
            throw new Error('No source to compile');
            */
            return Promise.reject(new Error('No source to compile.'));
        }
    })
    .then(() => {
        // Wait until the previous promise finished before starting the rcopyAsync
        return Promise.all([
            rcopyAsync(`${__dirname}/scripting`, 'build'),
            rcopyAsync(`${__dirname}/compiler/${process.platform}`, 'build'),
            rcopyAsync('src/scripting', 'build')
        ]);
    })
    ...
    

    您想推迟执行。如果您在函数包装器之外执行某些操作,那么它将立即执行。如果你把它放在函数包装器中,它只会在之前的调用完成后执行。

    【讨论】:

    • return Promise.resolve(true); 似乎不需要。
    • 我最初只是 c&p 源。我清理了一下。
    【解决方案2】:

    ES2017 有 async/await 语法

    防止Promise hell

    所以,你的代码应该是

    async function foo() {
        await rmrfAsync('{build,dist}')
    
        var files = await readAsync('src')  
        if (!files.length)
            throw new Error('No source to compile.');
    
        await Promise.all([
            rcopyAsync(`${__dirname}/scripting`, 'build'),
            rcopyAsync(`${__dirname}/compiler/${process.platform}`, 'build'),
            rcopyAsync('src/scripting', 'build')
        ])
        var files =  await globAsync('*.sma', { cwd: 'build' })
        console.log(files)
        return files
    }
    
    foo()
    .then(files => something(files))
    .catch(e => console.log('throw errors or rejecteds'))
    

    【讨论】:

      猜你喜欢
      • 2014-06-21
      • 2020-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-18
      • 2019-06-21
      • 2019-09-01
      • 2017-02-01
      相关资源
      最近更新 更多