使用Q承诺 – 有没有办法让这个代码重复性较低?

我正在为我的Node.js应用程序启动一个集成testing套件。 我目前正在尝试编写一个设置脚本来擦除testing数据库,并用一些testing数据填充它。 我试图避免可怕的“厄运金字塔” ,我希望使用承诺作为防止我的代码失控的一种方式。 我对承诺很陌生,我仍然试图把自己的头围绕在他们身上 – 可能我没有正确使用它们。

这是我没有承诺的最初版本。 这工作,但嵌套callback了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承诺的版本:

 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); }); 

我喜欢它嵌套较less,但在那里有很多重复。 有没有一种方法可以使用Q API中的辅助函数来使这个代码更简洁,重复性更低? 尤其是这部分:

 if (err) deferred.reject(new Error(err)); else deferred.resolve(); 

我将不胜感激清理这个代码的任何帮助。

 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); }); 

你可以用这样的可重复使用的助手来缩短它:

 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和yield关键字来彻底简化它。 这将实现一个asynchronous状态机,所以你可以有没有明确的callback伪线性代码stream。

看看async模块,特别是waterfall 。 你可以在这里读到它:

https://github.com/caolan/async#waterfalltasks-callback

基本上它可以让你以一个很好和简洁的方式链接一组嵌套的callback。