(Pode ser uma duplicata de # 312)
Um arquivo de migração 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) {
};
dá a seguinte saída:
{ __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)
Não acho que você deva retornar Promise.all([first, second]);
, uma vez que você já acorrentou second
a first
. Você poderia tentar apenas return first
? Como seu segundo createTable
está dentro de um then
que está encadeado em first
, não há necessidade de adicioná-lo a Promise.all
, pois ele será executado de qualquer maneira, enquanto first
está sendo resolvido.
A especificação de promessa diz que uma vez que uma promessa seja cumprida, ela nunca deve ser cumprida novamente e todas as invocações futuras de then
devem retornar o resultado armazenado em cache. Se first
e second
forem ambos cumpridos, Promise.all([first, second])
deve ser ~ um ambiente autônomo.
return first
tem o mesmo comportamento - ou seja, tenta criar as tabelas first
, first
e second
, que é _ainda mais errado_, como deveria nunca haja nenhum código que realmente avalie qualquer coisa dentro da função passada para first.then
nesse cenário.
Uhm ok, isso soa estranho. Não usar Promise.all
pode dar um pequeno impulso no desempenho;)
Mas sim, isso parece estar errado. Vou marcar isso como um bug por agora e esperar até que @tgriesser entre em
Haha, eu particularmente não me importo com o valor de um tick de desempenho em minhas migrações: p
Desculpe, fiz uma afirmação errada acima - return first
ainda deve criar a segunda tabela, só que o resultado da chamada de .then
no resultado da função não vai esperar por isso. O que, c / e - definitivamente ainda não deve fazer first
, first
, second
!
Não tenho certeza se entendi bem: Por que a 2ª mesa não esperaria pela criação da primeira? Uma vez que você interrompeu a primeira promessa usando then
eles serão efetivamente executados em série. Isso não deve ser um problema.
A criação da segunda tabela irá, sim, mas o resultado da função exports.up
não. Ou seja, se você chamar exports.up().then
, é o mesmo que chamar first.then
e não second.then
- porque _is_ first.then
!
Ah, não entendi você - sim, você está certo sobre isso!
Portanto, a confusão aqui é que o método createTable
não retorna uma promessa, mas sim um objeto SchemaBuilder
, que é um "thenable", ou seja, chamando .then
no objeto retornará uma promessa A + válida, mas o objeto em si não é uma promessa.
Isso foi feito especificamente para permitir que você use a sintaxe:
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 deve executar as migrações conforme o esperado na mesma conexão em sequência.
Além disso, não há necessidade de Promise.all
aqui, pois isso teria o mesmo efeito:
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');
});
});
};
embora, se você quisesse, você poderia fazer:
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]);
};
e as coisas funcionariam conforme definido pela especificação.
Ok, isso explica isso. Não sabia disso - desculpe a confusão por não ser capaz de dar uma resposta direta de imediato @SohumB
Sim, temos gráficos de dependência mais complicados do que isso; este foi apenas um caso de teste mínimo! Obrigado pela ajuda.
@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!
Comentários muito úteis
Portanto, a confusão aqui é que o método
createTable
não retorna uma promessa, mas sim um objetoSchemaBuilder
, que é um "thenable", ou seja, chamando.then
no objeto retornará uma promessa A + válida, mas o objeto em si não é uma promessa.Isso foi feito especificamente para permitir que você use a sintaxe:
que deve executar as migrações conforme o esperado na mesma conexão em sequência.
Além disso, não há necessidade de
Promise.all
aqui, pois isso teria o mesmo efeito:embora, se você quisesse, você poderia fazer:
e as coisas funcionariam conforme definido pela especificação.