我正在为我的Node.js应用启动集成测试套件。我目前正在尝试编写一个擦除测试数据库的安装脚本,并用一些测试数据填充它。我正试图避免可怕的"Pyramid of Doom",我希望使用promises来防止我的代码失控。我对承诺很新,我仍然试图绕过他们 - 我可能没有正确使用它们。
这是我没有承诺的初始版本。这有效,但嵌套回调了wazoo:
var mongoose = require('mongoose');
var User = require('./user');
var MONGODB_URL = process.env.MONGODB_TEST_URL || 'localhost:27017/swot_test';
console.log('\nRunning e2e test preparation script');
console.log('-----------------------------------\n');
console.log('Connecting to database:', MONGODB_URL, '...')
mongoose.connect(MONGODB_URL, function () {
console.log('Wiping database...')
mongoose.connection.db.dropDatabase(function () {
console.log('Setting up test user...')
User.createUser({
email: 'test@example.com',
password: 'tester'
}, function (err, user) {
if (err) throw err;
// If there was more setup, it would have to go here... pyramid of doom!
console.log('Finished.');
process.exit();
});
});
});
以下是使用Q promises的版本:
var Q = require('q');
var mongoose = require('mongoose');
var User = require('./user');
var MONGODB_URL = process.env.MONGODB_TEST_URL || 'localhost:27017/swot_test';
console.log('\nRunning e2e test preparation script');
console.log('-----------------------------------\n');
Q.fcall(function () {
var deferred = Q.defer();
console.log('Connecting to database:', MONGODB_URL, '...');
mongoose.connect(MONGODB_URL, function (err) {
if (err) deferred.reject(new Error(err));
else deferred.resolve();
});
return deferred.promise;
})
.then(function () {
var deferred = Q.defer();
console.log('Wiping database...');
mongoose.connection.db.dropDatabase(function (err) {
if (err) deferred.reject(new Error(err));
else deferred.resolve();
});
return deferred.promise;
})
.then(function () {
var deferred = Q.defer();
console.log('Setting up test user...');
User.createUser({
email: 'test@example.com',
password: 'tester'
}, function (err, user) {
if (err) deferred.reject(new Error(err));
else deferred.resolve();
});
return deferred.promise;
})
.done(function () {
console.log('Finished.');
process.exit();
}, function (err) {
console.error('An error occurred:', err.stack);
});
我喜欢它的嵌套较少,但那里有很多重复。有没有办法可以使用Q API中的辅助函数来使这些代码更简洁,重复性更低?特别是这部分:
if (err) deferred.reject(new Error(err));
else deferred.resolve();
感谢您清理此代码的任何帮助。
答案 0 :(得分:2)
Q.ninvoke(mongoose,'connect', MONGODB_URL)
.then(function () {
console.log('Wiping database...');
return Q.ninvoke(mongoose.connection.db, 'dropDatabase');
})
.then(function () {
console.log('Setting up test user...')
return Q.ninvoke(User, 'createUser', {
email: 'test@example.com',
password: 'tester'
});
})
.then(function (user) {
console.log('Finished.');
process.exit();
})
.catch(function(err) {
console.log(err);
});
答案 1 :(得分:0)
您可以使用可重复使用的oncomplete
帮助程序缩短它:
function oncomplete(deferred) {
return function (err, result) {
if (err) deferred.reject(new Error(err));
else deferred.resolve(result);
}
}
Q.fcall(function () {
var deferred = Q.defer();
console.log('Connecting to database:', MONGODB_URL, '...');
mongoose.connect(MONGODB_URL, oncomplete(deferred));
return deferred.promise;
})
.then(function () {
var deferred = Q.defer();
console.log('Wiping database...');
mongoose.connection.db.dropDatabase(oncomplete(deferred));
return deferred.promise;
})
.then(function () {
var deferred = Q.defer();
console.log('Setting up test user...');
User.createUser({
email: 'test@example.com',
password: 'tester'
}, oncomplete(deferred));
return deferred.promise;
})
.done(function () {
console.log('Finished.');
process.exit();
}, function (err) {
console.error('An error occurred:', err.stack);
});
如果您足够勇敢,可以使用Node v0.11 beta and yield
关键字大幅简化它。这将实现异步状态机,因此您可以在没有显式回调的情况下使用伪线性代码流。
答案 2 :(得分:-1)
查看async
模块,具体为waterfall
。你可以在这里阅读:
https://github.com/caolan/async#waterfalltasks-callback
基本上它允许你以一种简洁的方式链接一组嵌套的回调。