(Esto puede ser un duplicado del # 312)
Un archivo de migración como este:
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) {
};
da el siguiente resultado:
{ __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)
No creo que debas devolver Promise.all([first, second]);
, ya que ya has encadenado second
a first
. ¿Podrías intentar solo return first
? Dado que su segundo createTable
está dentro de un then
que está encadenado fuera de first
, no es necesario agregarlo a Promise.all
, ya que se ejecutará de todos modos mientras se resuelve first
.
La especificación de la promesa dice que una vez que se cumple una promesa, nunca debe volver a cumplirse, y todas las invocaciones futuras de then
deben devolver el resultado en caché. Si first
y second
se cumplen, entonces Promise.all([first, second])
debería ser ~ una operación no operativa.
return first
tiene el mismo comportamiento, es decir, intenta crear las tablas first
, first
y second
, lo cual es _aún más incorrecto_, como debería nunca habrá ningún código que realmente evalúe algo dentro de la función pasada a first.then
en ese escenario.
Uhm, eso suena raro. Sin embargo, no usar Promise.all
podría dar un pequeño impulso al rendimiento;)
Pero sí, esto parece estar mal. Lo marcaré como un error por ahora y esperaré hasta que @tgriesser intervenga . ¡Gracias por informar!
Jaja, no me importa particularmente el rendimiento de un tick en mis migraciones: p
Lo siento, hice una declaración incorrecta arriba: return first
aún debería crear la segunda tabla, es solo que el resultado de llamar a .then
en el resultado de la función no lo esperará. Lo cual, w / e - definitivamente no debería ser first
, first
, second
!
No estoy seguro de haber entendido completamente: ¿Por qué la segunda mesa no esperaría la creación de la primera? Dado que encadena la primera promesa usando then
, se ejecutarán efectivamente en serie. Eso no debería ser un problema.
La creación de la segunda tabla sí, pero el resultado de la función exports.up
no lo hará. Es decir, si llama a exports.up().then
, es lo mismo que llamar a first.then
y no a second.then
- ¡porque es _ first.then
!
Ah, te entendí mal allí, ¡sí, tienes razón en eso!
Entonces, la confusión aquí es que el método createTable
no devuelve una promesa, sino que devuelve un objeto SchemaBuilder
, que es un "thenable", es decir, llamar a .then
object devolverá una promesa A + válida, pero el objeto en sí mismo no es una promesa.
Esto se hizo específicamente para permitirle usar la sintaxis:
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
});
que debería ejecutar las migraciones como se esperaba en la misma conexión en secuencia.
Además, no hay necesidad de Promise.all
aquí, ya que esto lograría lo mismo:
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');
});
});
};
aunque si quisieras, podrías hacer:
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]);
};
y las cosas funcionarían según lo definido por la especificación.
Ok eso explica eso. No lo sabía, perdón por la confusión por no poder dar una respuesta directa de inmediato @SohumB
Sí, tenemos gráficos de dependencia más complicados que ese; ¡Este fue solo un caso de prueba mínimo! Gracias por la ayuda.
@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!
Comentario más útil
Entonces, la confusión aquí es que el método
createTable
no devuelve una promesa, sino que devuelve un objetoSchemaBuilder
, que es un "thenable", es decir, llamar a.then
object devolverá una promesa A + válida, pero el objeto en sí mismo no es una promesa.Esto se hizo específicamente para permitirle usar la sintaxis:
que debería ejecutar las migraciones como se esperaba en la misma conexión en secuencia.
Además, no hay necesidad de
Promise.all
aquí, ya que esto lograría lo mismo:aunque si quisieras, podrías hacer:
y las cosas funcionarían según lo definido por la especificación.