Knex: 承诺在迁移中的行为不符合预期

创建于 2014-06-16  ·  10评论  ·  资料来源: knex/knex

(这可能是#312的副本)

像这样的迁移文件:

exports.up = function(knex, Promise) {
  var first = knex.schema.createTable('first', function(table) {
    table.increments('id');
    table.string('name');
  });

  var second = first.then(function() {
    return knex.schema.createTable('second', function(table) {
      table.increments('id');
      table.string('name');
    });
  });

  return Promise.all([first, second]);
};

exports.down = function(knex, Promise) {

};

给出以下输出:

{ __cid: '__cid1',
  sql: 'create table "first" ("id" serial primary key, "name" varchar(255))',
  bindings: [] }
{ __cid: '__cid2',
  sql: 'create table "first" ("id" serial primary key, "name" varchar(255))',
  bindings: [] }
{ __cid: '__cid3',
  sql: 'create table "second" ("id" serial primary key, "name" varchar(255))',
  bindings: [] }
error: duplicate key value violates unique constraint "pg_type_typname_nsp_index"
    at Connection.parseE (/home/sohum/node_modules/pg/lib/connection.js:526:11)
    at Connection.parseMessage (/home/sohum/node_modules/pg/lib/connection.js:356:17)
    at Socket.<anonymous> (/home/sohum/node_modules/pg/lib/connection.js:105:22)
    at Socket.EventEmitter.emit (events.js:95:17)
    at Socket.<anonymous> (_stream_readable.js:745:14)
    at Socket.EventEmitter.emit (events.js:92:17)
    at emitReadable_ (_stream_readable.js:407:10)
    at emitReadable (_stream_readable.js:403:5)
    at readableAddChunk (_stream_readable.js:165:9)
    at Socket.Readable.push (_stream_readable.js:127:10)
question

最有用的评论

因此,这里的困惑在于createTable方法不会返回承诺,而是返回SchemaBuilder对象,该对象是“ thenable”,即在对象上调用.then对象将返回有效的A +承诺,但对象本身不是承诺。

专门这样做是为了允许您使用以下语法:

  return knex.schema.createTable('first', function(table) {
    table.increments('id');
    table.string('name');
  })
  .createTable('second', function(table) {
      table.increments('id');
      table.string('name');
  }).then(function() {
    // all done
  });

它将按顺序在同一连接上按预期运行迁移。

另外,这里不需要Promise.all ,因为这样做可以达到相同的效果:

exports.up = function(knex, Promise) {
  return knex.schema.createTable('first', function(table) {
    table.increments('id');
    table.string('name');
  }).then(function() {
    return knex.schema.createTable('second', function(table) {
      table.increments('id');
      table.string('name');
    });
  });
};

但是,如果您愿意,可以执行以下操作:

exports.up = function(knex, Promise) {
  var first = knex.schema.createTable('first', function(table) {
    table.increments('id');
    table.string('name');
  }).then(); // coerce thenable to a promise

  var second = first.then(function() {
    return knex.schema.createTable('second', function(table) {
      table.increments('id');
      table.string('name');
    });
  });

  return Promise.all([first, second]);
};

一切都会按照规范定义进行。

所有10条评论

我不认为您应该返回Promise.all([first, second]); ,因为您已经将secondfirst 。 您能不能只尝试return first吗? 由于您的第二个createTablethen ,而first已链接,因此无需将其添加到Promise.all ,因为它将被执行无论如何,在first正在解决中。

Promise规范说,一旦实现了Promise,就永远不能再次实现它,并且将来所有then调用都应返回缓存的结果。 如果firstsecond都已实现,则Promise.all([first, second])应该是〜no-op。

return first具有相同的行为-即,它尝试创建firstfirstsecond表,这甚至是错误的,因为应该在那种情况下,永远不会是任何实际评估传递给first.then的函数内任何内容的代码。

嗯,听起来确实很奇怪。 虽然不使用Promise.all可以稍微提高性能;)

但是,是的,这似乎是错误的。 我现在将其标记为错误,并等待@tgriesser发出提示音。感谢您的举报!

哈哈,在迁移过程中,我并不特别在乎一滴水的表现:p

抱歉,我在上面的语句中做错了- return first仍然应该创建第二个表,只是在函数的结果上调用.then的结果不会等待它。 w / e-应该绝对不要执行firstfirstsecond

不确定我是否完全理解:第二个表为什么不等待第一个表的创建? 由于您使用then断开了第一个承诺,因此它们将有效地连续运行。 那不应该是一个问题。

是,第二个表的创建将是,但是exports.up函数的结果将不会。 即,如果您调用exports.up().then ,则与调用first.then而不是调用second.then -因为它是_is_ first.then

啊,在那里误会了你-是的,你是对的!

因此,这里的困惑在于createTable方法不会返回承诺,而是返回SchemaBuilder对象,该对象是“ thenable”,即在对象上调用.then对象将返回有效的A +承诺,但对象本身不是承诺。

专门这样做是为了允许您使用以下语法:

  return knex.schema.createTable('first', function(table) {
    table.increments('id');
    table.string('name');
  })
  .createTable('second', function(table) {
      table.increments('id');
      table.string('name');
  }).then(function() {
    // all done
  });

它将按顺序在同一连接上按预期运行迁移。

另外,这里不需要Promise.all ,因为这样做可以达到相同的效果:

exports.up = function(knex, Promise) {
  return knex.schema.createTable('first', function(table) {
    table.increments('id');
    table.string('name');
  }).then(function() {
    return knex.schema.createTable('second', function(table) {
      table.increments('id');
      table.string('name');
    });
  });
};

但是,如果您愿意,可以执行以下操作:

exports.up = function(knex, Promise) {
  var first = knex.schema.createTable('first', function(table) {
    table.increments('id');
    table.string('name');
  }).then(); // coerce thenable to a promise

  var second = first.then(function() {
    return knex.schema.createTable('second', function(table) {
      table.increments('id');
      table.string('name');
    });
  });

  return Promise.all([first, second]);
};

一切都会按照规范定义进行。

好的,这可以解释。 不知道-对无法立即给出直接答案的困惑感到抱歉@SohumB

是的,我们有比这更复杂的依赖图; 这只是一个最小的测试用例! 谢谢您的帮助。

@johanneslumpe

sohum<strong i="8">@diurnal</strong> ~ % cat test.js
var Promise = require('bluebird');

function promises() {
  var first = Promise.resolve('wee');
  var second = first.then(function() {
    console.log('delayin');
    return Promise.delay(1000);
  }).then(function() {
    console.log('done!');
  });
  return first;
}

promises().then(function() { console.log('yep, first is finished'); });

sohum<strong i="9">@diurnal</strong> ~ % node test.js
delayin
yep, first is finished
done!
此页面是否有帮助?
0 / 5 - 0 等级

相关问题

npow picture npow  ·  3评论

rarkins picture rarkins  ·  3评论

nklhrstv picture nklhrstv  ·  3评论

mattgrande picture mattgrande  ·  3评论

mtom55 picture mtom55  ·  3评论