Knex: como configurar para usar o padrão ES6 Promise?

Criado em 22 jul. 2016  ·  80Comentários  ·  Fonte: knex/knex

ao executar com o nó v6, existe uma maneira de configurar para usar apenas o padrão ES6 Promise (livrar-se da dependência do bluebird)?

por exemplo, no pacote promise-queue, o padrão é usar o que estiver disponível globalmente Promise,

https://www.npmjs.com/package/promise-queue

ou o usuário pode configurá-lo explicitamente por Queue.configure(require('vow').Promise);

Então, este pacote pode implementar uma estratégia semelhante?

discussion

Comentários muito úteis

:+1: para promessas nativas.

Todos 80 comentários

Curioso: você deseja substituir uma biblioteca mais rápida por uma integrada mais lenta? No cliente não importaria, mas na velocidade do nó é um fator. Qual é o seu raciocínio para isso?

@johanneslumpe É um fator em alguns aplicativos, com knex Duvido muito que a biblioteca Promise tenha algum efeito significativo no desempenho. Foi discutido que devemos escrever todo o código Promise para usar apenas APIs A+ para que bluebird não seja necessário.

Depois disso, deve ser fácil substituir qual biblioteca Promise é usada.

Concordo que parece inútil em uma biblioteca como o knex. Dado que a base de código do knex já importa seu próprio arquivo promise.js internamente, tecnicamente ainda seria muito fácil de implementar, desde que a API seja a mesma _(o que não tenho certeza?)_. Nesse arquivo, pode-se usar como padrão as promessas globais, caso contrário, exigirá uma biblioteca configurada ou algo assim.

trata-se de dar aos usuários uma escolha; para usuários do Node V6+, dê a opção de gerenciar o mínimo possível de dependências de terceiros.

Curioso: você deseja substituir uma biblioteca mais rápida por uma integrada mais lenta?

o que você está dizendo pode ser verdade no passado, mas que tal agora ou 6 meses depois? (Eu fiz uma pesquisa e só consigo descobrir alguns benchmarks de 2015, se você tiver uma comparação mais recente, por favor poste alguns links)
Acredito que o objetivo de tornar o Promise o padrão ES6 é que as pessoas usem facilmente o Promise, não tendo que depender de uma biblioteca de terceiros, e a equipe principal do Node ou V8 não pode ficar cega para a diferença de desempenho para sempre, contanto que as licenças de código aberto dos dois projetos são compatíveis, podendo até emprestar algum código; ou apenas dê a eles algum tempo, acredito que o Promise integrado pode ser mais rápido e melhor.

consulte o AWS-SDK da Amazon: também é padrão usar qualquer Promise disponível globalmente; enquanto dá ao usuário a opção de configurar a biblioteca Promise favorita também

http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-making-requests.html#Support_for_Promises

é tudo sobre escolhas

Pensando bem, não será tão fácil quanto alterar um único arquivo. O Knex atualmente depende muito das funções utilitárias do Bluebird, como .timeout , .tap , .reduce , .map e assim por diante, que estou assumindo e esperando que não existam em ES6 Promessas.

Estou interessado em apoiar as promessas do ES6. Idealmente, precisaríamos de um segundo argumento para o construtor Knex que recebe um construtor Promise . A retrocompatibilidade seria alcançada assim:

const Promise = require('bluebird');
const knex = Knex(myKnexConfig, Promise);

Talvez possamos alias condicionalmente map , filter etc. com base em sua presença no Promise.prototype ?

Acho que isso deve estar bem baixo na lista de prioridades e exigiria algumas mudanças internas, sem mencionar a diminuição no desempenho (embora reconhecidamente não tenha visto benchmarks há algum tempo) e o fato de que as pessoas podem estar confiando no fato de que as promessas retornadas são um pássaro azul (para catch condicional, etc).

Eu estaria mais inclinado a esperar até que async/await aterrisse em um nó estável para resolver isso.

para as pessoas que querem as dependências mínimas de terceiros, o Promise nativo pode ser melhor,

como @google-cloud e muitas outras bibliotecas, você pode tornar padrão usar o Promise nativo e aceitar um parâmetro de promessa para quem deseja usar bibliotecas de terceiros

https://googlecloudplatform.github.io/google-cloud-node/#/docs/google -cloud/

var gcloud = require('google-cloud')({
  promise: require('bluebird')
});

@tgriesser Eu sei que esse é um problema antigo, mas agora temos async / await tanto no stable quanto no Carbon.

Dado que é preferível que o async/await trabalhe com promessas nativas ( de acordo com a especificação, as funções async devem retornar uma promessa nativa ) essa é uma prioridade mais alta?

Se o suporte de promessa nativa é algo que a Knex gostaria que acontecesse, mas não está atualmente no radar, um PR seria bem-vindo?

Obrigado pelo seu tempo.

As funções @malexdev async retornam promessas nativas que não têm muito a ver com o knex atualmente. Isso não significa que await precisa de promessas nativas para funcionar corretamente. Você poderia esclarecer em qual caso isso é um problema/benefício (exceto a eliminação de uma dependência)?

Dito isto, não sou contra a queda do bluebird, mas ele realmente precisa de algumas mudanças internas. Podemos precisar implementar alguns métodos que estão atualmente expostos de bluebird para APIs knex, a menos que a funcionalidade antiga esteja oculta em alguma opção de configuração e, por padrão, promessas nativas sejam usadas. Desta forma, a migração não seria impossível para as pessoas que estão relatando o fato de que o knex retorna bluebird promessas.

@elhigu O principal benefício para mim pessoalmente é que estou usando o TypeScript com uma regra TSLint impondo promessas nativas sendo usadas para await , então tenho que envolver todas as chamadas Knex dentro de um Promise.resolve() . Eu percebo que isso não tem nada a ver especificamente com o Knex, e provavelmente é um problema exclusivo para mim.

Fora isso, na minha opinião, ter menos dependências de terceiros é melhor e, como @c0b mencionou, mais opções nunca é uma coisa ruim.

Percebo que seria muito trabalho, o que é uma das razões pelas quais estou mais do que feliz em gastar tempo com isso, se é algo que Knex está interessado em avançar.

Sim, cheguei aqui vindo de um problema de datilografia - estou usando o Knex como o mecanismo SQL para minha biblioteca de armazenamento de dados multi-armazenamento e, embora seja ambivalente em relação às promessas nativas versus bluebird, não posso usar facilmente texto datilografado no knex por esta razão. Eu trato knex thenables como seguindo a especificação nativa (eu não uso nenhuma das extensões bluebird), mas o typescript me incomoda sobre retornar um Bluebirdquando a declaração do método é uma promessa.

Isso tem dois níveis de profundidade aqui, pois estamos lidando com a implementação de promessas e os tipos de knex (que são tratados por diferentes desenvolvedores), mas estou basicamente preso aqui - estou tecnicamente quebrando o contrato de tipo por retornando um Bluebird quando declarei uma Promise (acho que há coisas na API Promise que o Bluebird não suporta?) mas também não estou com vontade de colocar um monte de return Promise.resolve(bluebirdthing) em todos os lugares.

Eu gastei bastante tempo cavando em knex guts no ano passado e trabalhando com promessas em geral que eu estaria disposto a pegar algo aqui e trabalhar em um PR para modularizar a implementação do Promise se as pessoas quiserem - você estaria aberto a um PR? Acabaria sendo algo como o que @elhigu mencionou - reimplementando algumas das funções utilitárias para usar qualquer construtor Promise passado na instanciação para evitar a necessidade de reescrita de código. Não tenho certeza sobre o desempenho, é claro, mas isso é algo que pode ser comparado.

Ter tudo feito chique com async / await também seria legal, e não estou com pressa para corrigir isso (em última análise, para o meu caso de uso, acabo sinalizando as coisas como any e lidando com elas codebranchs como se fossem javascript em vez de typescript).

@ericeslinger Não vejo por que a implementação real do Promise que está sendo usada causaria problemas com o TypeScript, estou misturando bluebird e promessas nativas há um ano e meio sem problemas ...

Eu simplesmente não tenho usado nenhuma tipagem que introduziria tipos para o bluebird, apenas digo às tipagens datilografadas que elas são Promises normais e não vê nenhuma diferença (ofc. ele reclamará se eu tentar usar os métodos especiais do bluebird).

Qual versão do typescript e existe algum projeto de exemplo para reproduzir... por exemplo, github repo com npm start script.

Isso tem dois níveis de profundidade aqui, pois estamos lidando com a implementação de promessas e os tipos de knex (que são tratados por diferentes desenvolvedores), mas estou basicamente preso aqui - estou tecnicamente quebrando o contrato de tipo por retornando um Bluebird quando eu declarei um Promise (acho que há coisas na API do Promise que o Bluebird não suporta?)

Esses tipos de knex são do npm? As tipagens bluebird são do npm? Quais pacotes? Estou tendo dificuldade em entender por que isso aconteceria. Se houver um tipo Bluebird separado, ele deve ser herdado da promessa nativa e não há problema em retornar das APIs que informam que elas retornarão a promessa. Pela descrição, parece que o problema são implementações de digitação muito quebradas. E isso não é relevante para este problema (o typescript não se importa com tipos reais, então não saberá o que o knex retorna).

Estou obtendo minhas digitações do repositório Definitivamente Typed instalando @types/knex . Essa definição puxa com ela @types/bluebird , e todos os métodos knex são digitados como retornando objetos Bluebird.

Como algo específico, não posso fazer isso:

function mungeData(v: any): DataItem {} 
function foo(): Promise<DataItem[]> {
  return knex('data').select()
  .then((rows) => rows.map(row => mungeData(row)))
} // error, cannot return Bluebird<DataItem[]> as Promise<DataItem[]>

usando essas digitações. Isso ocorre porque knex.select().then() é digitado para retornar um Bluebird no repositório Definitivamente Typed, e esses encadeiam para criar mais Bluebirds, e dizendo algo como return foo as Promise<any>() quando foo é um Bluebirdfalhará (pelo menos falha no typescript 2.4), porque Bluebirds não são atribuíveis a Promises (eles não têm [Symbol.toStringTag] de acordo com this , então coagir um ao outro seria um erro, embora um pequeno erro).

Em vez disso, posso mudar para

function bar(): Promise<DataItem[]> {
  return Promise.resolve<DataItem[]>(foo())
}

ou faça outros truques para envolver todas as chamadas para knex dentro de uma chamada nativa Promise.resolve(). Isso fará com que o typescript pare de reclamar das funções da minha biblioteca, enquanto ainda me permite usar tipagens knex dentro das funções da minha biblioteca.

Antes de ontem, eu não tinha usado @types/knex - eu estava apenas digitando knex como any . O código funciona bem de qualquer maneira em tempo de execução (pelo menos para o meu caso de uso), é apenas

@elhigu : O problema não é implementações de digitação quebradas.
O TypeScript define o tipo para as funções async como Promise<[type]> , o que está correto de acordo com a especificação JS.
Knex retorna Bluebird<[type]> , que as digitações refletem com precisão.

Eu simplesmente não tenho usado nenhuma digitação que introduziria tipos para o bluebird, apenas digo às digitações datilografadas que são promessas normais e não vejo nenhuma diferença

Isso está mentindo para o compilador, pois as funções Knex realmente retornam Bluebird s. Não interessado.
Você está certo de que Bluebirds são compatíveis com Promises, mas parte do acordo com o TypeScript é que você realmente retorna o que diz que está retornando.

Ao retornar um Bluebird de uma função que foi digitada para retornar Promise , o TypeScript reclama porque o tipo Bluebird não é o mesmo que o tipo Promise .
Existem vários truques que podemos fazer (como o que @ericeslinger mencionou sobre usar any , ou envolver Promise.resolve() ), mas no final do dia truques como esse nos fazem perder muito o que o TypeScript fornece.

No final do dia, a realidade é que há pelo menos dois usuários agora que estão dizendo "Usar promessas nativas é importante para nós, e estamos dispostos a trabalhar para tornar a funcionalidade da promessa mais genérica".

Sei que você está apenas tentando ajudar, mas, francamente, em vez de ouvir "você poderia fazer assim", gostaria de saber se as mudanças de promessa propostas por mim / @ericeslinger / @c0b são aceitáveis ​​para que eu possa começar em um PR ou o quê.

@malexdev @ericeslinger Obrigado por mais informações! Parece que na verdade não é possível herdar sua própria classe de Promise então essa pode ser a razão pela qual retornar Bluebirds da função que é digitada como Promise<> falha :(

@ericeslinger De qualquer forma, isso não é um problema quando você cria funções async , pois elas automaticamente encapsulam os resultados em promessas nativas internamente. O seguinte está em conformidade sem problemas, com tipagens de @types/bluebird e compilado para ES2015 ou ESNEXT.

import * as Bluebird from 'bluebird';

// declaring function async converts bluebird implicitly to native Promise
async function asyncReturningPromise(): Promise<string> {
    const blueBirdPromise = new Bluebird<string>((resolve, reject) => { 
        resolve('yay asyncReturningPromise');    
    });
    return blueBirdPromise;
}

// main func to run the code using async / await
Bluebird.resolve().then(async () => {
    console.log("await function returning promise (bluebird)", await asyncReturningPromise());

    const blueBird = new Bluebird((resolve, reject) => { resolve(); });
    const returnedFromAsync = asyncReturningPromise();

    console.log("Bluebird instanceof Promise:", blueBird instanceof Promise);
    console.log("async retval instanceof Promise:", returnedFromAsync instanceof Promise);
});

saída:

await function returning promise (bluebird) yay asyncReturningPromise
Bluebird instanceof Promise: false
async retval instanceof Promise: true

Então, por enquanto, quando você estiver usando APIs knex, você realmente precisa dizer que está retornando o Bluebird, a menos que esteja usando funções / métodos assíncronos, que envolvem o bluebird automaticamente em Promises nativas.

@malexdev

Isso está mentindo para o compilador, pois as funções Knex realmente retornam Bluebirds. Não interessado.
Você está certo de que Bluebirds são compatíveis com Promises, mas parte do acordo com o TypeScript é que você realmente retorna o que diz que está retornando.

Na verdade, lidar com o typescript é que é suficiente que o objeto retornado implemente a interface corretamente, por exemplo, isso está perfeitamente bem:

class FakePromise<T> implements Promise<T>  {
    [Symbol.toStringTag]: "Promise";
    then<TResult1, TResult2>(onfulfilled?: (value: T) => TResult1 | PromiseLike<TResult1> | null | undefined, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2> | null | undefined): Promise<TResult1 | TResult2> {
        return new Promise((resolve, reject) => { resolve('Im totally broken and fake!); });
    }
    catch<TResult>(onrejected?: (reason: any) => TResult | PromiseLike<TResult> | null | undefined): Promise<T | TResult> {
        throw new Error("Method not implemented.");
    }
}

// this works and  fake promise instance has nothing to do with native promise
function returningPromiseInterface(): Promise<string> {
    const fakePromise = new FakePromise<string>();
    return fakePromise;
}

// compiling this fails, because looks like Bluebird actually doesn't implement Promise interface correctly
function asyncReturningPromise(): Promise<string> {
    const blueBirdPromise = new Bluebird<string>((resolve, reject) => { 
        resolve('yay asyncReturningPromise');    
    });
    return blueBirdPromise;
}

Isso atrapalha instanceof, mas o typescript na verdade nem prometeu que você retornou a instância nativa do Promise, apenas a interface.

Ao retornar um Bluebird de uma função que foi digitada para retornar Promise, o TypeScript reclama porque o tipo Bluebird não é o mesmo que o tipo Promise.
Existem vários truques que podemos fazer (como o que @ericeslinger mencionou sobre usar any, ou encapsular em Promise.resolve()), mas no final do dia truques como esse nos fazem perder muito do que o TypeScript fornece.

Eu odiaria ver pessoas tendo que fazer esse tipo de truque / alterar implementações de JS apenas para satisfazer digitações ruins.

No final do dia, a realidade é que há pelo menos dois usuários agora que estão dizendo "Usar promessas nativas é importante para nós, e estamos dispostos a trabalhar para tornar a funcionalidade da promessa mais genérica".

Sei que você está apenas tentando ajudar, mas, francamente, em vez de ouvir "você poderia fazer assim", gostaria de saber se as mudanças de promessa propostas por mim / @ericeslinger / @c0b são aceitáveis ​​para que eu possa começar em um PR ou o quê.

Obrigado pela compreensão :) Alterar o knex para usar promessas nativas já foi iniciado e implementado em algum momento do ano passado, então foi alterado de volta por @tgriesser , então eu diria que, por enquanto, é melhor não iniciar essa alteração.

Também ainda considero esses problemas de typescript mencionados neste tópico como problemas na declaração de tipagem (por que o bluebird não implementa o Promise corretamente... preciso me aprofundar nisso?), do que problemas na implementação do knex.

Dito isto, não me oponho a se livrar do bluebird em algum período de tempo, apenas vendo dois problemas separados aqui.

:+1: para promessas nativas.

@elhigu :

Na verdade, lidar com o typescript é que basta que o objeto retornado implemente a interface corretamente

Justo. Ainda mantenho minha opinião de que menos dependências e mais opções são melhores, mas agora vejo o que você quis dizer sobre tipagens quebradas.

Então ainda 👍 para Promises nativas (com as quais ainda estou disposto a ajudar), mas vejo agora que meu problema imediato pode ser resolvido corrigindo as tipagens do Bluebird. Obrigado pela informação.

Então, comecei a usar bastante o TypeScript e eu o adoro e agora percebo que os problemas aqui são um verdadeiro ponto de dor. À medida que o async/await ganha mais espaço no Node recente, o utilitário Bluebird fns ( map , reduce , tap , bind , return ) tornam-se menos úteis. Eu ficaria bem em continuar usando o Bluebird internamente, mas oficialmente "depreciando" a API pública do construtor de consultas knex retornando todos os métodos utilitários adicionais.

Com isso mesclado, poderíamos atualizar as definições do TypeScript para descartar as tipagens Bluebird (exceto toBluebird ) e alterar as tipagens Bluebird para as tipagens Promise.

Se alguém tiver largura de banda quiser resolver isso, aqui está o que estou pensando para um plano de ação:

  • [ ] Adicione um aviso de descontinuação para todos os métodos proxy do Bluebird ( tap , map , reduce , bind , return ).
  • [ ] Adicione um método .toBluebird() que será um caminho de migração para aqueles que desejam continuar usando o Bluebird (podem fazê-lo com um simples localizar/substituir todas as chamadas dos métodos acima e apenas adicioná-lo antes esses são chamados)
  • [ ] Depois que isso for mesclado/novo corte de lançamento (0.15), podemos atualizar as definições do Typescript
  • [ ] Eventualmente, podemos abandonar totalmente esses métodos. Essa API mais simples abre caminho para eventualmente usar Promises nativas e async/await quando fizer sentido.

Deixe-me saber se isso faz sentido e se alguém está interessado em tentar isso.

Definitivamente estaria interessado em ajudar com isso.

Isso significa que o Knex começaria a manter suas próprias definições de TypeScript? Isso nos permitiria fazer algumas coisas legais com genéricos que as tipagens geradas automaticamente nunca suportarão.

Comecei este fork como uma primeira tentativa de adicionar suporte para Promises nativas:
https://github.com/tgriesser/knex/pull/2523/files

Curta este comentário de 2016:

Curioso: você deseja substituir uma biblioteca mais rápida por uma biblioteca integrada mais lenta

Incrível o quanto pode mudar em 2 anos.

Ao retornar um Bluebird de uma função que foi digitada para retornar Promise, o TypeScript reclama porque o tipo Bluebird não é o mesmo que o tipo Promise.

@malexdev Na verdade, o typescript usa tipagem estrutural (o fluxo usa tipagem nominal e funcionaria da maneira que você descreve), desde que seu tipo atenda à interface Promise , ele é compatível com Promise , seja explicitamente extends / implements isso ou não.

Como isso está progredindo? Eu acho que um bom primeiro passo seria fatorar as chamadas de métodos específicos do Bluebird dentro do knex (ou seja, não removê-lo ainda). A remoção do bluebird e o fornecimento de uma opção para um construtor Promise personalizado seguiriam (e daria às pessoas que usam os métodos do Bluebird um caminho de atualização).

Não começo a trabalhar no primeiro passo se não houver objeções. O trabalho existente parece ter morrido.

@qubyte Eu não acho que haja um esforço ativo para fazer a mudança, mudanças incrementais foram feitas aqui e ali, mas é isso.

OK. No meu próximo tempo livre, farei algumas pequenas alterações possíveis para fatorar cada método.

@tgriesser Alguma opinião quando devemos avançar com este (se alguma vez)? Para mim, o próximo mês de abril parece um momento razoável para quando o Node 6 LTS chegar ao fim da linha.

Informações interessantes em 2018:

promises-native-async-await tem melhor desempenho que promises-bluebird no nó 10.
Referência: https://github.com/petkaantonov/bluebird/tree/master/benchmark

Portanto, o desempenho não é mais uma razão para manter o bluebird. Devemos ir para async/await.

promessas-nativo-async-await tem melhor desempenho

isso também é o que eu acreditei fortemente em 2016 que a maneira nativa melhoraria muito mais rápido, apenas porque é o núcleo da comunidade Nodejs, tem mais pessoas se preocupando com isso, mais do que qualquer biblioteca de terceiros

Enquanto o ticket foi arquivado pedindo escolhas, existem tantas implementações Promise concorrentes, não é bom apostar no bluebird para sempre

Existe alguma atualização sobre isso?

@cfaoulis O mesmo ainda está de pé. Quando abril chegar, podemos abandonar o suporte para o Node 6 e começar a remover o bluebird.

alguma atualização de 2019? /cc para alguns contribuidores ou mantenedores principais ou qualquer pessoa @aqui que se importe com isso: @johanneslumpe @tgriesser @wubzz @elhigu de https://github.com/tgriesser/knex/graphs/contributors?type=c&from=2018-01- 01&para=2019-12-31

Por outro lado, a comunidade JavaScript é um mundo tão dinâmico, vibrante e às vezes cruel, a cada 3 ou 2 anos (ou até mais rápido) há substituições para algo que estávamos familiarizados antes, pense em Grunt, Gulp => Webpack, as ferramentas, bibliotecas, os frameworks estão competindo totalmente em todos os níveis, então, para bibliotecas mais antigas, se você estiver parando de trazer inovações ou diminuindo o suporte a novos padrões (pense em iteradores async/await do ES2019...), você eventualmente será substituído

Acabei de fazer uma pesquisa simples, parece que no nível do DB ORM também existem muitas alternativas, o TypeORM pode ser uma boa ... ( paro por aqui para não dizer mais ...)
https://bestofjs.org/tags/db
https://bestofjs.org/projects/typeorm

@c0b não há necessidade de cc. Recebo e-mails de todos os comentários de qualquer maneira. @kibertoad acabou de dizer tudo o que tinha a ser dito em seu último comentário ... Este problema também não tem nada a ver com knex suportando recursos async/await ES2019 melhores e knex não é um ORM, então não tenho certeza do que esse comentário era realmente cerca de.

Se você precisa de um bom ORM, posso recomendar object.js. Também é implementado em cima do knex, aqui está um tópico muito bom sobre isso https://github.com/Vincit/objection.js/issues/1069

Em algum momento este knex será substituído, mas não por nenhum ORM. Ele pode ser substituído por algum outro construtor de consultas, que tenha uma base de código mais limpa e uma API mais consistente. Como por exemplo knex 1.0 talvez ;)

Além disso, se o knex for substituído, eu ficaria totalmente bem com isso, menos trabalho para mim: D

Há WIP nisso eu acredito: https://github.com/tgriesser/knex-next

Só queria mencionar também que não usar promessas nativas resulta em https://github.com/nodejs/node/issues/22360 ao usar async_hooks o que faz com que o contexto atual seja perdido.

Confie em mim, não precisamos de motivos adicionais para nos mudar, queremos fazê-lo tão mal quanto todos vocês :). No entanto, ainda precisamos liberar mais algumas correções para o branch Node 6 e, então (finalmente), vamos abandoná-lo e começar a eliminar gradualmente o bluebird.

Depois que o #3227 for mesclado, podemos finalmente começar!

Eu sei que você mencionou antes que poderia usar ajuda nesta migração, se essa ainda for a direção que você deseja seguir, podemos ajudar de alguma forma?

Estou pensando: faça um projeto, adicione algumas tarefas e veja se alguém (talvez eu tenha tempo) pode ser atribuído e definir algumas datas?

@chaffeqa Criará algumas tarefas mais granulares em breve, tenha #3250 para uma primeira rodada de mudanças fáceis. Principalmente, precisamos substituir os usos bluebird.tap, bluebird.method e bluebird.try por algo nativo. Se você já tiver algum tempo, você pode tentar ramificar #3250 e dar uma olhada em qualquer 'bluebird' restante (eu recomendaria começar com os não específicos de dialeto para que você possa validar rapidamente a funcionalidade ainda funcionando executando test:sqlite sem qualquer configuração do Docker).

@qubyte Se você gostaria de contribuir, agora é a hora!

@kibertoad estou seguro para usar async/await agora?

Você quer dizer na base de código knex? Certo. Na sua você sempre foi :-D

Desculpe por estar desaparecido na semana passada, as coisas estão aumentando para nossa empresa, então estou tendo que me concentrar nisso.

Queria fechar o ciclo em algumas das discussões que tivemos sobre um dos maiores blocos para a atualização: substituir o Disposer usage .

É um buraco bem profundo quando você começa a descer, então será preciso uma boa engenharia para fornecer uma boa cópia / abstração. Eu me preocupo que a sobrecarga de desempenho de algo possa ser muito grande (muitos mais objetos criados à medida que a cadeia de promessas cresce).

Na verdade, comecei em alguns POCs, e acho que este é o mais direto deles:

class DisposablePromise extends Promise {

  disposerFunc = null;
  originalResource = null;

  then(onFulfilled, onRejected) {
    const $onFulfilled = this.wrap(onFulfilled);
    return super.then($onFulfilled, onRejected).copyContext(this);
  }

  copyContext(promise) {
    this.disposerFunc = promise.disposerFunc;
    this.originalResource = promise.originalResource;
    return this;
  }

  disposer(disposerFunc) {
    this.disposerFunc = disposerFunc
  }

  isDisposable() {
    return !!this.disposerFunc
  }

  wrap(onFulfilled: any) {
    const $onFulfilled = (result: any) => {
      if (this.disposerFunc && !this.originalResource) {
        this.originalResource = result
      }
      if (result instanceof Promise) {
        return onFulfilled(result);
      } else {
        const res = onFulfilled(result)
        if (this.disposerFunc) {
          this.disposerFunc(this.originalResource)
        }
        return res
      }
    };

    return $onFulfilled;
  }
}

E outro:

      var DisposablePromise = function DisposablePromise() {
          var self = DisposablePromise.convert(Promise.resolve());
          return self;
      };
      DisposablePromise.convert = function convert(promise, props) {
          promise.__proto__ = DisposablePromise.prototype;
          return props ? Object.assign(promise, props) : promise;
      };
      DisposablePromise.prototype = Object.create(Promise.prototype);
      DisposablePromise.prototype.constructor = DisposablePromise;
      DisposablePromise.prototype.then = function then(resolve, reject) {
          var returnVal = Promise.prototype.then.call(this, resolve, reject);
          return DisposablePromise.convert(returnVal);
      };
      DisposablePromise.prototype.catch = function _catch(err) {
          var returnVal = Promise.prototype.catch.call(this, err);
          return DisposablePromise.convert(returnVal);
      };
      DisposablePromise.prototype.finally = function finall(obj) {
          var returnVal = Promise.prototype.finally.call(this, obj);
          return DisposablePromise.convert(returnVal);
      };
      DisposablePromise.prototype.disposer = function disposer(disposerFunc) {
        var returnVal = Promise.prototype.finally.call(this, obj);
        return DisposablePromise.convert(returnVal);
      };

Mas não tive tempo para prová-los.

Acho que pode valer a pena explorar outras opções (manter o bluebird, mas convertê-lo para usar promessas nativas internamente?) Adoraria ouvir qualquer pensamento da equipe do bluebird, mesmo sobre abstrair essa funcionalidade, embora meu instinto diga que está bastante ligado aos ganchos de implementação do bluebird.

Eu diria que se conseguirmos descobrir essa parte, o resto dessas tarefas é bem simples.

@chaffeqa Np, agradeço que você ainda encontre tempo para voltar a isso!
Duvido muito que as pessoas do bluebird estejam abertas a sugestões para reengenharia seriamente sua implementação, eles reiteraram repetidamente o ponto de que, neste momento, estão interessados ​​em estabilidade acima de tudo, e recomendam que as pessoas realmente usem promessas nativas, a menos que realmente precisem de recursos avançados fornecido pela Bluebird.
Considerando que o Node 8 parece ser a versão mais popular do Node.js no momento (com base nas estatísticas oficiais de download do Node.js), temo que ainda não possamos migrar para a abordagem baseada em iterador assíncrono.
Quais desvantagens você vê na implementação do DisposablePromise pela Knex? Como ele estende o Promise nativo, presumo que ele não traga nenhuma das desvantagens do Bluebird, e nada no espaço do usuário precisa saber sobre isso?

@ericeslinger FWIW, as tipagens TS não devem mais ser um problema no master, estamos digitando nossas promessas como nativas agora para desencorajar os usuários a confiar nos recursos do Bluebird. Isso pode causar problemas no futuro quando Promises nativas implementam algo que o Bluebird promete não, então ainda queremos substituir as promessas usadas o máximo possível. Qualquer contribuição nesse sentido seria muito apreciada :)

Nozes eu imaginei tanto 😞
Concordo que fazer algo como DisposablePromise é provavelmente o caminho a seguir neste caso, especialmente porque o item realmente necessário ainda está em proposta .

A desvantagem é que será muito importante projetar algo como DisposablePromise de maneira criteriosa... e francamente nem sei se minha implementação funciona 😆 (tenho tanta dificuldade em pensar assíncrono por algum motivo ha).

Se houver mais alguém neste tópico que gostaria de dar uma facada nesta questão <3 u longtimes!

@chaffeqa Quão complicada é a implementação do Bluebird? Talvez possamos simplesmente extraí-lo e adicionar em cima da promessa nativa?

@chaffeqa Pior cenário - podemos remover todos os outros usos do Bluebird e manter este devido à sua complexidade se considerarmos muito arriscado. Não é o ideal, mas eventualmente using vai acontecer.

infelizmente bastante complicado ... a implementação pega carona no fato de que o bluebird controla o ciclo de vida da promessa. Eu acho que a melhor abordagem é ver o que ele está tentando fazer (que é bem próximo do link em using acima) e criar um calço o mais simples e eficiente possível para ele.

O problema é que o pipeline deve ser uma promessa de estilo Bluebird , que se entendi corretamente, não adere ao desempenho da promessa nativa (e, portanto, você perde todo o rastreamento + funcionalidade assíncrona nativa).

Eu prefiro fazer algo que, sob o capô, use promessas nativas para as partes assíncronas, mas forneça a capacidade de vincular contexto e implementar o uso necessário como disposer .

Para sua informação, outra coisa em minha mente é: na verdade, há um uso mínimo de usage e .disposer no knex, então talvez a abordagem funcione melhor para mover isso para um nível mais alto?

Vale a pena experimentar :)

oooo também uma opção que encontrei com base em https://github.com/petkaantonov/bluebird/issues/1593

De qualquer forma, acho que um bom passo à frente foi o que você começou em um branch anterior, onde isolamos todo o uso de Promise que na verdade é um BluebirdPromise , dessa forma podemos começar a brincar com a queda em substituições como DisposablePromise ou BluebirdNativePromise .

@chaffeqa Você quer dizer a parte Bluebird.setScheduler(fn => Promise.resolve().then(fn)) ?
A conversão geral está ocorrendo muito bem! Se pudéssemos manter os Disposers no Bluebird enquanto os fazíamos usar promessas nativas sob o capô, isso poderia ser uma boa solução.

Só queria mencionar também que não usar promessas nativas resulta em nodejs/node#22360 ao usar async_hooks que faz com que o contexto atual seja perdido.

A solução alternativa é usar o patch https://github.com/TimBeyer/cls-bluebird .

Apenas para informação, o LTS para Node v8 termina este ano.

@Bessonov Contexto? Como o nó mínimo para 10 afeta esse problema? Observe que já descartamos o suporte ao nó 6.

Eu não estou familiarizado com a base de código knex, mas talvez existam alguns recursos, que podem ajudá-lo a se livrar do bluebird. Por exemplo, o nó 10 tem suporte para Promise.finally .

Mas de qualquer forma, estou feliz em ver o progresso neste tópico :+1:

Sobre o padrão de eliminação - poderíamos apenas adicionar um retorno de chamada opcional para as coisas, que retornam a promessa descartável?
(Assim como nas transações)

getDisposableConnection(config, cb) {
    const connection = await getConnection(config)

   // user want autodisposable connection
    if (cb) 
      Promise.resolve(cb(connection)).then(() => connection.dispose())
   // user will dispose by himself
   return connection
}

Qual nível de promessa de independência de biblioteca precisamos?
1) todos usam promessas nativas
2) promessas nativas internas, o usuário pode definir a própria lib de promessa para a interface
3) o usuário pode definir lib de promessa para internos e interface

Qual é o estado atual desta questão. Geralmente, o knex funciona agora com o async await, mas o typescript relatará um aviso de que estamos aguardando um método que não é uma promessa nativa.

Então, para responder à pergunta do problema original. A solução alternativa atual é simplesmente aguardar e adicionar algo como // tslint:disable-next-line: await-promise

@maximelkin Eu voto na opção 1. A longo prazo, espero que todas as bibliotecas de promessas sejam obsoletas.

id segundo que, neste momento, estamos além da promessa de polyfills, mesmo para a maioria dos navegadores

@Bessonov atualmente no knex depende de bibliotecas (e talvez projetos), o que requer exatamente bluebird

devemos dar alguma solução alternativa para eles

@Bessonov atualmente no knex depende de bibliotecas (e talvez projetos), o que requer exatamente bluebird, devemos fornecer alguma solução de fallback para eles

Não importa se os usuários do knex são dependentes do bluebird. O Knex ainda pode usar promessas nativas e elas irão interoperar muito bem com promessas do bluebird. Nós absolutamente não devemos dar qualquer fallback.

Portanto, esse problema começou com a solicitação de recurso com a escolha da implementação da promessa.
Do nada, mudou para remover o bluebird sem motivo e quebrar todos os dependentes. Sem qualquer aviso, changelog, opção de fallback e versão principal.

Mas suponho que todos os usuários do 1,5 datilografado estejam felizes agora.

Portanto, esse problema começou com a solicitação de recurso com a escolha da implementação da promessa.
Do nada, mudou para remover o bluebird sem motivo e quebrar todos os dependentes. Sem qualquer aviso, changelog, opção de fallback e versão principal.

Pelo menos as versões anteriores do knex 0.x foram consideradas como lançamentos principais com alterações potencialmente perigosas, portanto, apenas a atualização para 0.20.x deveria ser considerada uma atualização segura (semver é realmente solto quando o número da versão < 1).

A remoção do bluebird está na mesa há muito tempo, não se trata apenas desse problema.

removendo bluebird sem motivo

A remoção do bluebird não foi à toa. Você ainda pode usar o bluebird externamente com promessas do knex. Um grande motivo para descartar o bluebird foi que as funções async criam implicitamente promessas nativas, então, no futuro, continuar usando o Bluebird exigiria que um código de encapsulamento extra do bluebird fosse adicionado na API knex sem motivo algum.

Sem qualquer aviso, changelog,

Acordado. Eu vasculhei os últimos changelogs... Infelizmente parece que nós falhamos em listar as mudanças importantes entre as versões. Precisamos ter mais cuidado ao escrever changelogs para realmente apontar as mudanças, o que quebra as APIs antigas. Por exemplo, muitas das alterações de digitação realmente quebrarão o código TS antigo.

Houve o mesmo problema no projeto ioredis https://github.com/luin/ioredis/commit/da60b8b. Eles queriam oferecer suporte a promessas nativas - e os caras fizeram uma solução muito boa - eles adicionaram uma opção para oferecer suporte a qualquer biblioteca de promessas personalizada e usam promessa nativa por padrão. Por que não? A configuração de uma biblioteca de promessa personalizada é rápida e não requer a aplicação de patches em todo o código do aplicativo.

continuar usando o Bluebird exigiria que um código extra de encapsulamento do bluebird fosse adicionado na API knex sem motivo algum.

Sim. Mas por que não agrupar chamadas de módulo no bluebird (ou em qualquer outra biblioteca de promessas) se foi explicitamente especificado? Esse é um wrapper simples, sem sobrecarga, e permitiria que os usuários usassem qualquer biblioteca de promessas que quisessem. Se ninguém precisar do bluebird, ninguém usaria essas opções, e você pode descontinuar com segurança a tempo.

Além disso, eu vi uma opinião que

A longo prazo, espero que todas as bibliotecas de promessas sejam obsoletas.

Mas IMHO existem duas suposições erradas:

  • Bluebird é usado porque é mais rápido.
  • Bluebird é usado como um pollyfill.

Acho que esse não é o caso de aplicativos realmente complexos, que vão além dos forros de espera assíncrona.
O Bluebird possui muitos recursos que são absolutamente necessários para fluxos assíncronos complexos - como tempos limite, tratamento de erros personalizado, mapeamento com simultaneidade, cancelamento, redução e assim por diante. Todos esses recursos podem ser implementados em promessas nativas, mas isso é muito clichê inútil. Em 2020, ainda estamos usando o bluebird no Node 12 porque não queremos todo esse clichê.

Por que não? A configuração de uma biblioteca de promessa personalizada é rápida e não requer a aplicação de patches em todo o código do aplicativo.

Qualquer coisa que use async-await internamente vai forçar promessas para promessas nativas, então suas opções se tornam envolver a saída de cada método na promessa personalizada ou banir async-await no código interno. Não é um empreendimento tão pequeno quanto pode parecer à primeira inspeção.

@qubyte

Não é um empreendimento tão pequeno quanto pode parecer à primeira inspeção.

Não, isso é tão simples como eu já disse. Você cria um wrapper para funções externas exportadas e isso é tudo. Cerca de 10 linhas de código. E escreva todo o código interno da maneira que quiser.

@jehy : Sinta-se à vontade para enviar um PR para essas 10 linhas de código se você encontrar uma maneira direta de implementá-las.

Eu também passarei algum tempo hoje tentando encontrar uma solução alternativa.

Por que vale a pena, grande parte da API do bluebird é duplicada com a mesma ou próxima API usando promessas nativas desses pacotes: https://github.com/sindresorhus/promise-fun

Pelo que vale, grande parte da API do bluebird é duplicada com a mesma ou próxima API usando promessas nativas desses pacotes

~ 50 pacotes em vez de 1? A sério?

Sim, embora na maioria das vezes apenas alguns sejam necessários (p-map, por exemplo). Sua milhagem pode variar, é claro. Ele é oferecido apenas como uma rota potencial para o que você deseja.

@jehy : Aqui está algo que você pode tentar como solução temporária no código do seu aplicativo:

const Bluebird = require('bluebird');


const prototypesNeedingDecoration = [
  require('knex/lib/query/builder').prototype,
  require('knex/lib/schema/builder').prototype,
  require('knex/lib/transaction').prototype,
  require('knex/lib/raw').prototype,
];

const corePromiseMethods = ["then", "catch", "finally"];


function decoratePromiseMethods(target) {
  for(const m of corePromiseMethods) {
    const original = target[m];

    target[m] = function(...args) {
      return Bluebird.resolve(original.apply(this, args))
    }
  }  
}

function hackBluebird() {
  for(const target of prototypesNeedingDecoration) {
    decoratePromiseMethods(target);
  }
}


hackBluebird();

Esta não é realmente uma solução adequada para o problema geral. Existem outros objetos temporários criados em knex que precisariam ser decorados de maneira semelhante.

Além disso, isenção de responsabilidade: a solução alternativa ☝️ teve muito poucos testes. Portanto, você deve executar novamente os testes do seu aplicativo para garantir que nada tenha quebrado.

Só queria adicionar meus 2 centavos aqui: eu realmente aprecio todo o trabalho colocado nessa migração, independentemente do feedback negativo.

Do ponto de vista do nosso aplicativo, o knex foi a última biblioteca que nos forçou a exigir o Bluebird, e a conformidade com o suporte completo à promessa nativa significa que:

  1. não temos mais rastreamentos de pilha manchados
  2. reduzimos nosso peso SSR em uma quantidade decente
  3. melhoramos o desempenho, pois o async async nativo agora tem mais desempenho que o bluebird (e está crescendo cada vez mais!)

é uma grande vitória continuar a correr em direção ao padrão es... e eu sei que não é fácil para os mantenedores de bibliotecas, então eu queria gritar para vocês e obrigado por assumir esse fardo!

para aqueles que sofrem com a mudança: eu adoraria ajudar, pois nos beneficiamos, então entre em contato se precisar de ajuda para depurar ou migrar!

@chaffeqa Obrigado por este feedback, significa muito!

@jehy : Você já teve a chance de tentar contornar o que foi proposto? Se sim, resolveu seus problemas imediatos?

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

nklhrstv picture nklhrstv  ·  3Comentários

arconus picture arconus  ·  3Comentários

mtom55 picture mtom55  ·  3Comentários

aj0strow picture aj0strow  ·  3Comentários

legomind picture legomind  ·  3Comentários