learningtech
learningtech

Reputation: 33705

Stop sequelize promise chain without call back hell

I'm new to using node js, so it's very likely I misunderstand the concept of "promise" and "callback hell". In any case, I need suggestions on how to avoid the following code:

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

Ignore the fact that I'm creating tables with SQL instead of using Sequelize migration scripts, because I'm just trying to illustrate the point that I have A LOT of mysql queries that should run in series. If a query fails, then I need to stop the entire script and not let the subsequent .then() function fire. In my Sequelize code, I achieved this by nesting a lot of raw query function calls, then and catch statements. This is going to be very difficult to troubleshoot if I have 100 of these nested callback statements.

Are there alternatives for me to consider beside nesting all these callback functions?

Upvotes: 1

Views: 1216

Answers (3)

halfer
halfer

Reputation: 20439

In order to avoid "promise hell" - which is just the same problem as "callback hell" - one can return each Promise inside a top-level thenable:

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

The Promise system allows chaining in this fashion, which removes the need for the high level of nesting and indentation. Note also that only one catch is required - if a thenable fails, it skips forward to the next available catch().

Upvotes: 0

robertklep
robertklep

Reputation: 203359

Sequelize uses (a modified version of) the bluebird promises library, which means that this should work:

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

It uses the static version of .each(), which will iterate over the array items sequentially, pass each to the runQuery iterator (which returns a promise), and will stop when a promise is rejected.

Upvotes: 1

Merv
Merv

Reputation: 154

Did you not already answer your own question by not using migration scripts? By default, you`d want to run migration scripts to set up your database and have it logged so you know when you migrated or when you last migrated.

If you need sequential SQL commands, you can still do that within 1 command. The query will run sequential anyway. If you want to have every single table be a model, make migration scripts for that model, don`t do it like this.

Upvotes: 0

Related Questions