I am currently in the process of converting a build process with some callback hell into promise hell (hell most likely due to me being new to promises and my inexperience with Bluebird). I am struggling to get past the .all
method with errors that a file already exists. Perhaps the file copies within it are attempting to happen before rimraf
at the start?
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;
});
For anyone interested, the working portion of the callback hell is below:
...
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);
});
});
});
});
});
});
You are being tricked by how promises work. When you first execute your javascript, all of the parts of the chain are built. Because, in building your chain, you call rcopyAsync
, rcopyAsync
will start immediately. If you want something to occur later, you need to wrap it in a .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')
]);
})
...
You want to defer your execution. If you do something outside of a function wrapper, then it will be executed immediately. If you put it inside the function wrapper, it will only be executed once the previous call finishes.