停止续订承诺链而不回电地狱

时间:2015-08-23 20:18:14

标签: node.js callback sequelize.js

我是使用节点js的新手,所以我很可能会误解“承诺”和“回调地狱”的概念。无论如何,我需要有关如何避免以下代码的建议:

var Sequelize = require('sequelize');
var DB = new Sequelize('project1db', 'john', 'password123', {
  host: 'localhost',
  dialect: 'mysql'
});


var DB_PREFIX = 't_';

DB.query(
'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'user` ( ' +
'`user_id` int(11) UNSIGNED NOT NULL' +
') ENGINE=InnoDB DEFAULT CHARSET=utf8;',{type: DB.QueryTypes.RAW})
.then(function(results) {
    DB.query(
    'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'organization` ( ' +
    '`organization_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT ' +
    ') ENGINE=InnoDB DEFAULT CHARSET=utf8; ', {type:DB.QueryTypes.RAW})
    .then(function(results) {
        DB.query(
        'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'user_organization` ( ' +
        '`user_id` int(11) UNSIGNED NOT NULL ' +
        ') ENGINE=InnoDB DEFAULT CHARSET=utf8; ')
        .then(function(){
            DB.query(
            'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'content` ( ' +
            '`content_id` int(11) UNSIGNED NOT NULL ' +
            ') ENGINE=InnoDB DEFAULT CHARSET=utf8; ', {type:DB.QueryTypes.RAW})
            .then(function(){
            // more queries
            }).catch(function(err){console.log(err);});
        }).catch(function(err){console.log(err);});
    }).catch(function(err){console.log(err);});
}).catch(function(err){console.log(err);});

忽略我使用SQL而不是使用Sequelize迁移脚本创建表的事实,因为我只是想说明我有很多应该串行运行的mysql查询。如果查询失败,那么我需要停止整个脚本,不要让后续的.then()函数触发。在我的Sequelize代码中,我通过嵌套大量原始查询函数调用,然后和catch语句来实现这一点。如果我有100个这样的嵌套回调语句,这将很难进行故障排除。

除了嵌套所有这些回调函数之外,我还有其他选择吗?

3 个答案:

答案 0 :(得分:1)

Sequelize使用bluebird promises库的(修改后的版本),这意味着它应该有效:

var Promise = Sequelize.Promise;

Promise.each([
  'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'user` ( ' +
    '`user_id` int(11) UNSIGNED NOT NULL' +
    ') ENGINE=InnoDB DEFAULT CHARSET=utf8;',
  'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'organization` ( ' +
    '`organization_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT ' +
    ') ENGINE=InnoDB DEFAULT CHARSET=utf8; ',
  'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'user_organization` ( ' +
    '`user_id` int(11) UNSIGNED NOT NULL ' +
    ') ENGINE=InnoDB DEFAULT CHARSET=utf8; ',
  'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'content` ( ' +
    '`content_id` int(11) UNSIGNED NOT NULL ' +
    ') ENGINE=InnoDB DEFAULT CHARSET=utf8; ',
], function runQuery(query) {
  return DB.query(query, { type: DB.QueryTypes.RAW });
}).then(function() {
  console.log('all done');
}).catch(function(err) {
  console.log(err);
});

它使用.each()的静态版本,它将按顺序迭代数组项,将每个项传递给runQuery迭代器(返回一个promise),并在promise被拒绝时停止。

答案 1 :(得分:0)

您是否已经通过不使用迁移脚本回答了自己的问题?默认情况下,您需要运行迁移脚本来设置数据库并将其记录下来,以便了解迁移时间或上次迁移时间。

如果您需要顺序SQL命令,您仍然可以在1个命令中执行此操作。无论如何,查询将顺序运行。如果您希望将每个表都作为模型,请为该模型制作迁移脚本,不要这样做。

答案 2 :(得分:0)

为了避免出现“回调地狱”(与“回调地狱”相同的问题),可以将每个Promise返回到顶层可伸缩的容器中:

DB.query(
    'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'user` ( ' +
    '`user_id` int(11) UNSIGNED NOT NULL' +
    ') ENGINE=InnoDB DEFAULT CHARSET=utf8;',{type: DB.QueryTypes.RAW})
.then(function(results) {
    return DB.query(
        'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'organization` ( ' +
        '`organization_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT ' +
        ') ENGINE=InnoDB DEFAULT CHARSET=utf8; ', {type:DB.QueryTypes.RAW})
}).then(function(results) {
    return DB.query(
        'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'user_organization` ( ' +
        '`user_id` int(11) UNSIGNED NOT NULL ' +
        ') ENGINE=InnoDB DEFAULT CHARSET=utf8; ')
}).then(function(){
    return DB.query(
        'CREATE TABLE IF NOT EXISTS `'+DB_PREFIX+'content` ( ' +
        '`content_id` int(11) UNSIGNED NOT NULL ' +
        ') ENGINE=InnoDB DEFAULT CHARSET=utf8; ', {type:DB.QueryTypes.RAW})
}).then(function(){
     // more queries
})
.catch(function(err){console.log(err);});

Promise系统允许以这种方式链接,从而消除了对高级嵌套和缩进的需求。还要注意,只需要一个捕获-如果一个thenable失败,它将向前跳到下一个可用的catch()