使用Q promises - 有没有办法让这些代码重复性降低?

时间:2014-02-02 03:09:21

标签: javascript node.js mongoose promise q

我正在为我的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();

感谢您清理此代码的任何帮助。

3 个答案:

答案 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

基本上它允许你以一种简洁的方式链接一组嵌套的回调。