Knex: Обещания не работают должным образом при миграции

Созданный на 16 июн. 2014  ·  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 , который является "пригодным для использования", т.е. вызывает .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]); , поскольку вы уже связали second с first . Не могли бы вы попробовать только return first ? Поскольку ваш второй createTable находится внутри then который связан с first , нет необходимости добавлять его в Promise.all , поскольку он будет выполнен в любом случае, пока решается first .

Спецификация обещания гласит, что после выполнения обещания оно никогда не должно выполняться снова, и все будущие вызовы then должны возвращать кешированный результат. Если first и second оба выполнены, то Promise.all([first, second]) должно быть ~ нерабочим.

return first ведет себя так же - то есть пытается создать таблицы first , first и second , что _еще более неверно_, как и должно быть никогда не должен быть код, который фактически оценивает что-либо внутри функции, переданной в first.then в этом сценарии.

Хм, ладно, звучит странно. Однако отказ от использования Promise.all может немного повысить производительность;)

Но да, это кажется неправильным. Я сейчас отмечу это как ошибку и подожду, пока не

Ха-ха, меня не особо заботит производительность одного тика в моих миграциях: p

Извините, я сделал неверное утверждение выше - return first все равно должен создать вторую таблицу, просто результат вызова .then для результата функции не дождется этого. Что, ж / д - он все равно определенно не должен делать first , first , second !

Не уверен, что полностью понимаю: почему бы второй таблице не дождаться создания первой? Поскольку вы связываете первое обещание с помощью then они будут эффективно выполняться последовательно. Это не должно быть проблемой.

Да, вторая таблица будет создана, но результат функции exports.up нет. То есть, если вы вызываете exports.up().then , это то же самое, что и вызов first.then а не second.then - потому что это _is_ first.then !

Ах, там вас неправильно поняли - да, в этом вы правы!

Таким образом, путаница здесь заключается в том, что метод createTable не возвращает обещание, а возвращает объект SchemaBuilder , который является "пригодным для использования", т.е. вызывает .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 рейтинги