Knex: ¿Cómo configurar para usar la promesa ES6 estándar?

Creado en 22 jul. 2016  ·  80Comentarios  ·  Fuente: knex/knex

cuando se ejecuta con el nodo v6, ¿hay alguna forma de configurar para usar solo la Promesa ES6 estándar (eliminar la dependencia de bluebird)?

por ejemplo, en el paquete Promise-queue, por defecto se usa cualquier Promesa disponible globalmente,

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

o el usuario puede configurarlo explícitamente por Queue.configure(require('vow').Promise);

Entonces, ¿puede este paquete implementar una estrategia similar?

discussion

Comentario más útil

:+1: para promesas nativas.

Todos 80 comentarios

Curioso: ¿quieres reemplazar una biblioteca más rápida con una integrada más lenta? En el cliente no importaría, pero en la velocidad del nodo es un factor. ¿Cuál es tu razonamiento para esto?

@johanneslumpe Es un factor en algunas aplicaciones, con knex Dudo mucho que la biblioteca Promise utilizada tenga algún efecto significativo en el rendimiento. Se ha discutido que deberíamos escribir todo el código Promise para usar solo API A+ para que no se necesiten bluebird .

Después de eso, debería ser fácil anular qué biblioteca de Promise se usa.

Estoy de acuerdo en que se siente inútil en una biblioteca como knex. Dado que el código base de knex ya importa su propio archivo promise.js internamente, técnicamente sería muy fácil de implementar, siempre que la API sea la misma _(¿que no estoy seguro de que sea?)_. En este archivo, uno podría usar promesas globales por defecto, de lo contrario, requerir una biblioteca configurada, o algo así.

se trata de dar a los usuarios una opción; para los usuarios de Node V6+, dé la opción de administrar las dependencias de terceros lo mínimo posible.

Curioso: ¿quieres reemplazar una biblioteca más rápida con una integrada más lenta?

lo que dices podría ser cierto en el pasado, pero ¿qué tal ahora o 6 meses después? (Hice una búsqueda y solo puedo encontrar algunos puntos de referencia de 2015, si tiene una comparación más reciente, publique algunos enlaces)
Creo que el objetivo principal de hacer de Promise el estándar ES6 es que las personas usen Promise fácilmente, sin tener que depender de una biblioteca de terceros, y el equipo central de Node o V8 no puede ignorar la diferencia de rendimiento para siempre, siempre y cuando las licencias de código abierto de los dos proyectos son compatibles, incluso pueden tomar prestado algo de código; o simplemente deles algo de tiempo. Creo que la Promesa incorporada puede ser más rápida y mejor.

consulte AWS-SDK de Amazon: también utiliza de forma predeterminada cualquier Promesa que esté disponible a nivel mundial; mientras le da al usuario la opción de configurar la biblioteca Promise favorita también

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

se trata de opciones

Pensándolo bien, no será tan fácil como cambiar un solo archivo. Actualmente, Knex depende mucho de las funciones de utilidad de Bluebird, como .timeout , .tap , .reduce , .map , etc., que supongo y espero que no existan. en ES6 Promesas.

Estoy interesado en apoyar las promesas de ES6. Idealmente necesitaríamos un segundo argumento para el constructor Knex que toma un constructor Promise . La retrocompatibilidad se lograría así:

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

¿Quizás podríamos condicionalmente alias map , filter etc. en función de su presencia en Promise.prototype proporcionados?

Creo que esto debería estar bastante abajo en la lista de prioridades, y requeriría bastantes cambios internos, sin mencionar la disminución en el rendimiento (aunque es cierto que no he visto puntos de referencia en mucho tiempo) y el hecho de que la gente podría estar confiando en el hecho de que las promesas devueltas son un pájaro azul (para catch condicionales, etc.).

Estaría más inclinado a esperar hasta que async/await aterrice en un nodo estable para tratar de abordar esto.

para las personas que desean las dependencias mínimas de terceros, la Promesa nativa puede ser mejor,

como @google-cloud y muchas otras bibliotecas, puede configurar de forma predeterminada el uso de Promise nativo y aceptar un parámetro de promesa para quienes deseen usar bibliotecas de terceros

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

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

@tgriesser Sé que este es un problema antiguo, pero ahora tenemos async / await tanto en estable como en Carbon.

Dado que se prefiere que async/await funcione con promesas nativas ( según la especificación, las funciones async deben devolver una promesa nativa ), ¿es esta una prioridad más alta?

Si el soporte de promesa nativa es algo que a Knex le gustaría que sucediera, pero actualmente no está en el radar, ¿sería bienvenido un PR?

Gracias por tu tiempo.

Las funciones @malexdev async devuelven promesas nativas, no tiene mucho que ver con knex actualmente. No significa que await necesite promesas nativas para funcionar correctamente. ¿Podría aclarar en qué caso se trata de un problema/beneficio (a excepción de eliminar una dependencia)?

Dicho esto, no estoy en contra de eliminar bluebird, pero realmente necesita algunos cambios internos. Es posible que debamos implementar algunos métodos que actualmente están expuestos desde bluebird hasta las API knex, a menos que la funcionalidad antigua esté oculta bajo algún cambio de configuración y, de forma predeterminada, se usarían promesas nativas. De esta manera, migrar no sería imposible para las personas que han estado confiando en el hecho de que knex devuelve bluebird promesas.

@elhigu El principal beneficio para mí personalmente es que estoy usando TypeScript con una regla TSLint que hace cumplir las promesas nativas que se usan para await , por lo que tengo que envolver todas las llamadas Knex dentro de Promise.resolve() . Sin embargo, me doy cuenta de que esto no tiene nada que ver con Knex específicamente, y es probable que sea un problema exclusivo para mí.

Aparte de eso, en mi opinión, tener menos dependencias de terceros es mejor, y como @c0b mencionó, tener más opciones nunca es algo malo.

Me doy cuenta de que sería mucho trabajo, que es una de las razones por las que estoy más que feliz de dedicar tiempo a esto, si es algo en lo que Knex está interesado en avanzar.

Sí, llegué aquí debido a un problema de mecanografiado: estoy usando Knex como el motor SQL para mi biblioteca de almacén de datos de almacenamiento múltiple, y aunque soy ambivalente con respecto a las promesas nativas frente a bluebird, no puedo usar fácilmente mecanografiado en knex por este motivo. Trato a los thenables knex como si siguieran las especificaciones nativas (no uso ninguna de las extensiones de bluebird), pero el mecanografiado me molesta acerca de devolver un Bluebirdcuando la declaración del método es una promesa.

Esto tiene una profundidad de dos niveles aquí, ya que estamos lidiando tanto con la implementación de la promesa como con los tipos de knex (que son manejados por diferentes desarrolladores), pero básicamente estoy atrapado aquí: técnicamente estoy rompiendo el contrato de tipo por devolver un Bluebird cuando he declarado una Promise (supongo que hay cosas en la API de Promise que Bluebird no admite), pero tampoco tengo ganas de poner un montón de return Promise.resolve(bluebirdthing) todas partes.

Pasé suficiente tiempo investigando las entrañas de Knex durante el último año y trabajando con promesas en general de que estaría dispuesto a tomar algo aquí y trabajar en un PR para modularizar la implementación de Promise si la gente quiere. abierto a un PR? Terminaría siendo algo así como lo que mencionó @elhigu : volver a implementar algunas de las funciones de utilidad para usar cualquier constructor de Promise que se haya pasado en la creación de instancias para evitar las necesidades de reescritura de código. No estoy seguro sobre el rendimiento, por supuesto, pero eso es algo que se puede comparar.

Tenerlo todo arreglado con async / await también sería genial, y no tengo prisa por arreglar esto (en última instancia, para mi caso de uso, termino marcando las cosas como any y lidiando con eso ramas de código como si fueran javascript en lugar de mecanografiado).

@ericeslinger No veo por qué la implementación real de Promise que se está utilizando podría causar problemas con TypeScript, he estado mezclando bluebird y native promises durante un año y medio sin ningún problema...

Simplemente no he estado usando ninguna tipificación que introduzca tipos para bluebird, solo le digo a las tipificaciones mecanografiadas que son Promesas normales y no veo ninguna diferencia (por supuesto, se quejará si intento usar los métodos especiales de bluebird).

Qué versión mecanografiada y hay algún proyecto de ejemplo para reproducir... por ejemplo, github repo con npm start script.

Esto tiene una profundidad de dos niveles aquí, ya que estamos lidiando tanto con la implementación de la promesa como con los tipos de knex (que son manejados por diferentes desarrolladores), pero básicamente estoy atrapado aquí: técnicamente estoy rompiendo el contrato de tipo por devolver un Bluebird cuando he declarado una Promise (supongo que hay cosas en la API de Promise que Bluebird no admite), pero tampoco tengo ganas de poner un montón de return Promise.resolve (bluebirdthing) en todas partes.

¿Son esos tipos de knex de npm? ¿Las tipificaciones de bluebird son de npm? ¿Qué paquetes? Estoy teniendo dificultades para entender por qué sucedería eso. Si hay un tipo de Bluebird separado, debe heredarse de la promesa nativa y estar bien para regresar de las API que indican que devolverán la Promesa. Por la descripción, parece que el problema es que las implementaciones de tipeo están muy rotas. Y eso no es relevante para este problema (mecanografiado no se preocupa por los tipos reales, por lo que no sabrá qué devuelve knex).

Obtengo mis tipeos del repositorio DefinitelyTyped mediante la instalación @types/knex . Esa definición atrae @types/bluebird , y todos los métodos knex se escriben como objetos Bluebird que regresan.

Como cosa específica, no puedo hacer esto:

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[]>

utilizando estas tipificaciones. Esto se debe a que se escribe knex.select().then() para devolver un Bluebird en el repositorio DefinitelyTyped, y esos se encadenan para crear más Bluebirds, y dicen algo como return foo as Promise<any>() cuando foo es un Bluebirdfallará (al menos falla en TypeScript 2.4), porque los Bluebirds no se pueden asignar a Promises (carecen de [Symbol.toStringTag] de acuerdo con this , por lo que obligar a uno al otro sería un error, aunque sea un pequeño error).

En cambio, puedo cambiar a

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

o haga otros trucos para envolver todas las llamadas a knex dentro de una llamada Promise.resolve() nativa. Esto hará que TypeScript deje de quejarse aguas abajo de las funciones de mi biblioteca, al mismo tiempo que me permite usar tipeos knex dentro de las funciones de mi biblioteca.

Antes de ayer, no había usado @types/knex en absoluto, solo estaba escribiendo knex como any . El código funciona bien de cualquier manera en tiempo de ejecución (al menos para mi caso de uso), es solo

@elhigu : el problema no se rompe al escribir implementaciones.
TypeScript establece el tipo para las funciones async como Promise<[type]> , lo cual es correcto según las especificaciones de JS.
Knex devuelve Bluebird<[type]> , que las escrituras reflejan con precisión.

Simplemente no he estado usando ninguna tipificación que introduzca tipos para bluebird, solo le digo a las tipificaciones mecanografiadas que son Promesas normales y no veo ninguna diferencia.

Esto le está mintiendo al compilador, ya que las funciones Knex en realidad devuelven Bluebird s. No interesado.
Tiene razón en que Bluebirds es compatible con Promises, pero parte del trato con TypeScript es que en realidad devuelve lo que dice que está devolviendo.

Al devolver un Bluebird de una función que se ha escrito para devolver Promise , TypeScript se queja porque el tipo Bluebird no es lo mismo que el tipo Promise .
Hay varios trucos que podemos hacer (como lo que @ericeslinger mencionó sobre usar any , o envolver en Promise.resolve() ) pero al final del día, trucos como ese nos hacen perder gran parte de lo que proporciona TypeScript.

Al final del día, la realidad es que ahora hay al menos dos usuarios que dicen "Usar promesas nativas es importante para nosotros y estamos dispuestos a trabajar para hacer que la funcionalidad de la promesa sea más genérica".

Me doy cuenta de que solo está tratando de ayudar, pero, francamente, en lugar de escuchar "podría hacerlo de esta manera", me gustaría saber si los cambios de promesa propuestos por mí / @ericeslinger / @c0b son aceptables para que pueda comenzar en un PR o qué.

@malexdev @ericeslinger ¡ Gracias por más información! Parece que en realidad no es posible heredar su propia clase de Promise por lo que esa podría ser la razón por la cual falla la devolución de Bluebirds de la función que se escribe como Promesa <> :(

@ericeslinger De todos modos, esto no es un problema cuando crea funciones async , ya que ajustan automáticamente los resultados a las promesas nativas internamente. Lo siguiente cumple sin problemas, con tipeos de @types/bluebird y compilado en ES2015 o 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);
});

producción:

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

Entonces, por ahora, cuando usa las API de knex, necesita decir que está devolviendo Bluebird a menos que esté usando funciones/métodos asíncronos, que envuelven bluebird automáticamente a las promesas nativas.

@malexdev

Esto le está mintiendo al compilador, ya que las funciones de Knex en realidad devuelven Bluebirds. No interesado.
Tiene razón en que Bluebirds es compatible con Promises, pero parte del trato con TypeScript es que en realidad devuelve lo que dice que está devolviendo.

En realidad, tratar con mecanografiado es que es suficiente que el objeto devuelto implemente la interfaz correctamente, por ejemplo, esto está perfectamente bien:

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;
}

Confunde la instancia de, pero TypeScript en realidad ni siquiera prometió que devolvió la instancia nativa de Promise, solo la interfaz.

Al devolver un Bluebird de una función que se ha escrito para devolver Promise, TypeScript se queja porque el tipo Bluebird no es lo mismo que el tipo Promise.
Hay varios trucos que podemos hacer (como lo que @ericeslinger mencionó sobre usar cualquiera o envolver en Promise.resolve()), pero al final del día, trucos como ese nos hacen perder gran parte de lo que proporciona TypeScript.

Odiaría ver a la gente tener que hacer ese tipo de trucos/cambiar las implementaciones de JS solo para satisfacer las malas tipificaciones.

Al final del día, la realidad es que ahora hay al menos dos usuarios que dicen "Usar promesas nativas es importante para nosotros y estamos dispuestos a trabajar para hacer que la funcionalidad de la promesa sea más genérica".

Me doy cuenta de que solo está tratando de ayudar, pero, francamente, en lugar de escuchar "podría hacerlo de esta manera", me gustaría saber si los cambios de promesa propuestos por mí / @ericeslinger / @c0b son aceptables para que pueda comenzar en un PR o qué.

Gracias por entender :) El cambio de knex para usar promesas nativas ya se inició e implementó en algún momento el año pasado, luego @tgriesser lo cambió de nuevo, así que diría que por ahora es mejor no comenzar este cambio.

Además, sigo considerando estos problemas de mecanografiado mencionados en este hilo como problemas en la declaración de tipos (¿por qué bluebird no implementa Promise correctamente... necesito profundizar más en eso?), que como problemas en la implementación de knex.

Dicho esto, no me opongo a deshacerme de bluebird en algún momento solo viendo dos problemas separados aquí.

:+1: para promesas nativas.

@elhigu :

En realidad, tratar con mecanografiado es que es suficiente que el objeto devuelto implemente la interfaz correctamente

Lo suficientemente justo. Todavía mantengo mi opinión de que menos dependencias y más opciones es mejor, pero ahora veo lo que quiso decir con los tipos rotos.

Así que todavía 👍 para Promesas nativas (con las que todavía estoy dispuesto a ayudar), pero ahora veo que mi problema inmediato se puede resolver arreglando los tipos de Bluebird. Gracias por la info.

Así que comencé a usar TypeScript bastante y me encanta y ahora me doy cuenta de que los problemas aquí son un verdadero punto de dolor. A medida que async/await gana más terreno en Nodeland reciente, la utilidad Bluebird fns ( map , reduce , tap , bind , return ) se vuelven menos útiles. Estaría bien si continuara usando Bluebird internamente pero oficialmente "desaprobando" la API pública del generador de consultas knex que devuelve todos los métodos de utilidad adicionales.

Con eso fusionado, podríamos actualizar las definiciones de TypeScript para eliminar los tipos de Bluebird (excepto toBluebird ) y cambiar los tipos de Bluebird a tipos de Promesa.

Si alguien tiene ancho de banda y quiere abordar esto, esto es lo que estoy pensando para un plan de acción:

  • [ ] Agregue una advertencia de obsolescencia para todos los métodos con proxy de Bluebird ( tap , map , reduce , bind , return ).
  • [ ] Agregue un método .toBluebird() que será una ruta de migración para aquellos que quieran continuar usando Bluebird (pueden hacerlo con una simple búsqueda/reemplazo de todas las llamadas de los métodos anteriores y simplemente agregarlo antes esos se llaman)
  • [ ] Una vez que esto se fusione/corte de nueva versión (0.15), podemos actualizar las definiciones de Typescript
  • [ ] Con el tiempo, podemos abandonar estos métodos por completo. Esta API más simple allana el camino para usar eventualmente Promises nativos y async/await cuando tenga sentido.

Avíseme si esto tiene sentido y si alguien está interesado en intentarlo.

Definitivamente estaría interesado en ayudar con esto.

¿Significa esto que Knex comenzaría a mantener sus propias definiciones de TypeScript? Nos permitiría hacer algunas cosas geniales con genéricos que los tipos generados automáticamente nunca admitirán.

Comencé esta bifurcación como un primer intento de agregar soporte para Promesas nativas:
https://github.com/tgriesser/knex/pull/2523/files

Me gusta este comentario de 2016:

Curioso: desea reemplazar una biblioteca más rápida con una incorporada más lenta

Increíble lo mucho que puede cambiar en 2 años.

Al devolver un Bluebird de una función que se ha escrito para devolver Promise, TypeScript se queja porque el tipo Bluebird no es lo mismo que el tipo Promise.

@malexdev En realidad, TypeScript usa escritura estructural (el flujo usa escritura nominal y funcionaría de la manera que usted describe), por lo que siempre que su tipo cumpla con la interfaz Promise , es compatible con Promise , ya sea explícitamente extends / implements si o no.

¿Cómo está progresando esto? Considero que un buen primer paso sería eliminar las llamadas de método específicas de Bluebird dentro de knex (es decir, no eliminarlo todavía). A continuación, se eliminaría bluebird y se brindaría una opción para un constructor de Promise personalizado (y se brindaría a las personas que usan métodos de Bluebird una ruta de actualización).

No empiezo a trabajar en el primer paso si no hay objeciones. El trabajo existente parece haberse extinguido.

@qubyte No creo que haya un esfuerzo activo para hacer el cambio, se hicieron cambios incrementales aquí y allá, pero eso es todo.

Está bien. En mi próximo tiempo libre, haré algunos cambios pequeños como sea posible para eliminar cada método.

@tgriesser ¿ Alguna opinión sobre cuándo deberíamos seguir adelante con este (si alguna vez)? Para mí, el próximo abril parecería un momento razonable cuando el Nodo 6 LTS llegue al final de la línea.

Información interesante en 2018:

promises-native-async-await tiene mejor rendimiento que promises-bluebird en el nodo 10.
Referencia: https://github.com/petkaantonov/bluebird/tree/master/benchmark

Por lo tanto, el rendimiento ya no es una razón para conservar bluebird. Deberíamos optar por async/await.

Promises-native-async-await tiene un mejor rendimiento

eso es también lo que creía firmemente en 2016 que la forma nativa mejoraría mucho más rápido, solo porque es el núcleo de la comunidad de Nodejs, tiene más personas que se preocupan por él, más que cualquier biblioteca de terceros.

Si bien, el ticket se presentó solicitando opciones, hay tantas implementaciones Promise que compiten, simplemente no es bueno apostar por bluebird para siempre

¿Hay alguna actualización sobre esto?

@cfanoulis Lo mismo sigue en pie. Cuando llegue abril, podemos eliminar el soporte para Node 6 y comenzar a eliminar bluebird.

alguna actualizacion 2019? /cc a algunos colaboradores principales o mantenedores o cualquier persona @aquí a quien le importe: @johanneslumpe @tgriesser @wubzz @elhigu de https://github.com/tgriesser/knex/graphs/contributors?type=c&from=2018-01- 01&to=2019-12-31

Por otro lado, la comunidad de JavaScript es un mundo tan dinámico, vibrante y, a veces, cruel, cada 3 o 2 años (o incluso más rápido) hay reemplazos para algo que conocíamos antes, piense en Grunt, Gulp => Webpack, las herramientas, bibliotecas, los marcos compiten completamente en todos los niveles. Por lo tanto, para las bibliotecas más antiguas, si deja de incorporar innovaciones o ralentiza el soporte de nuevos estándares (piense en los iteradores async/await de ES2019...) eventualmente será reemplazado

Acabo de hacer una investigación simple, parece que a nivel DB ORM también hay muchas alternativas, TypeORM podría ser una buena... (Me detengo aquí para no decir más...)
https://bestofjs.org/tags/db
https://bestofjs.org/projects/typeorm

@c0b no hay necesidad de cc. Recibo correos electrónicos de todos los comentarios de todos modos. @kibertoad acaba de decir todo lo que tenía que decir en su último comentario... Este problema tampoco tiene nada que ver con que knex admita funciones async/await ES2019 mejores y knex no es un ORM, así que no estoy seguro de qué era ese comentario realmente sobre.

Si necesita un buen ORM, puedo recomendar objeción.js. También se implementa sobre knex, aquí hay un hilo bastante bueno al respecto https://github.com/Vincit/objection.js/issues/1069

En algún momento se reemplazará este knex, pero no con ningún ORM. Podría ser reemplazado por algún otro generador de consultas, que tiene una base de código más limpia y una API más consistente. Como por ejemplo knex 1.0 tal vez;)

Además, si se reemplaza knex, estaría totalmente de acuerdo con eso, menos trabajo para mí: D

Solo quería mencionar también que no usar promesas nativas da como resultado https://github.com/nodejs/node/issues/22360 cuando se usa async_hooks lo que hace que se pierda el contexto actual.

Confía en mí, no necesitamos razones adicionales para mudarnos, queremos hacerlo tan mal como todos ustedes :). Sin embargo, todavía tenemos que publicar un par de correcciones más para la rama del Nodo 6 y luego (finalmente), la descartaremos y comenzaremos a eliminar gradualmente a bluebird.

Después de fusionar #3227, ¡finalmente podemos comenzar!

Sé que mencionaste antes que podrías necesitar ayuda en esta migración, si esa sigue siendo la dirección que quieres seguir, ¿podríamos ayudarte de alguna manera?

Estoy pensando: hacer un proyecto, agregar un par de tareas y ver si alguien (tal vez tengo tiempo) podría ser asignado y establecer algunas fechas.

@chaffeqa Creará algunas tareas más granulares pronto, tenga #3250 listo para una primera ronda de cambios fáciles. Principalmente necesitamos reemplazar los usos de bluebird.tap, bluebird.method y bluebird.try con algo nativo. Si ya tiene algo de tiempo, puede intentar bifurcarse en el n. ° 3250 y echar un vistazo a los requisitos restantes de 'bluebird' (le recomendaría comenzar con los que no son específicos del dialecto para que pueda validar rápidamente la funcionalidad que aún funciona ejecutando test:sqlite sin ninguna configuración de Docker).

@qubyte Si desea contribuir, ¡ahora es el momento!

@kibertoad , ¿puedo usar async/await ahora?

¿Quieres decir en la base de código knex? Por supuesto. En la tuya siempre estuviste :-D

Perdón por no haber estado presente la semana pasada, las cosas se están acelerando para nuestra empresa, así que tengo que concentrarme allí.

Queríamos cerrar el ciclo en parte de la discusión que tuvimos sobre uno de los bloques más grandes de la actualización: reemplazar el uso Disposer .

Es un trabajo bastante profundo cuando comienzas a bajarlo, por lo que se necesitará una buena ingeniería para proporcionar una buena copia/abstracción. Me preocupa que la sobrecarga de rendimiento de algo pueda ser bastante grande (se crean muchos más objetos a medida que crece la cadena de promesas).

De hecho, comencé con algunos POC, y creo que este es el más sencillo de ellos:

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;
  }
}

Y otro:

      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);
      };

Pero no he tenido tiempo de probarlos.

Creo que puede valer la pena explorar otras opciones (¿mantener bluebird pero convertirlo para usar promesas nativas internamente?) Debido al hecho de que esta función debe estar en el repositorio (a menos que pueda pensar en mejores enfoques... ¿iteradores asíncronos? Me encantaría escuchar alguna opinión del equipo de bluebird, incluso sobre la abstracción de esa funcionalidad, aunque mi instinto dice que está bastante ligado a los ganchos de implementación de bluebird.

Diría que si podemos resolver esta parte, el resto de esas tareas son bastante sencillas.

@chaffeqa Np, ¡agradezco que aún encuentres tiempo para volver a esto!
Dudo mucho que la gente bluebird esté abierta a sugerencias para rediseñar seriamente su implementación, han reiterado repetidamente el punto de que en este punto están interesados ​​​​en la estabilidad por encima de todo, y recomiendan a las personas que realmente usen promesas nativas a menos que uno realmente necesite funciones avanzadas. proporcionado por Blue Bird.
Teniendo en cuenta que Node 8 parece ser la versión más popular de Node.js en este momento (según las estadísticas oficiales de descarga de Node.js), me temo que todavía no podemos pasar a un enfoque basado en iteradores asíncronos.
¿Qué desventajas le ve a Knex implementando DisipablePromise internamente? Dado que extiende la Promesa nativa, supongo que no trae ninguno de los inconvenientes de Bluebird, y nada en el espacio de usuario necesita saber al respecto.

@ericeslinger FWIW, los tipos de TS ya no deberían ser un problema en el maestro, estamos escribiendo nuestras promesas como las nativas ahora para disuadir a los usuarios de confiar en las características de Bluebird. Esto puede causar problemas en el futuro cuando las promesas nativas implementen algo que las promesas de Bluebird no hacen, por lo que aún queremos reemplazar las promesas usadas tanto como sea posible. Cualquier contribución en este sentido sería muy apreciada :)

Loco, pensé tanto 😞
Estoy de acuerdo en que hacer algo como DisposablePromise es probablemente el camino a seguir en este caso, especialmente porque el artículo que realmente se necesita todavía está en propuesta .

La desventaja es que va a ser muy importante diseñar algo como DisposablePromise de manera juiciosa... y, francamente, ni siquiera sé si mi implementación funciona 😆 (tengo muchos problemas para pensar de forma asíncrona por alguna razón decir ah).

Si hay alguien más en este hilo que quisiera intentar resolver este problema <3 ¡desde hace mucho tiempo!

@chaffeqa ¿Qué tan complicada es la implementación de Bluebird? ¿Tal vez podamos simplemente extraerlo y agregarlo además de la promesa nativa?

@chaffeqa En el peor de los casos: podemos eliminar todos los demás usos de Bluebird y conservar este debido a su complejidad si consideramos que es demasiado arriesgado tocarlo. No es ideal, pero eventualmente using va a suceder.

desafortunadamente bastante complicado ... la implementación se basa en el hecho de que bluebird controla el ciclo de vida de la promesa. Creo que el mejor enfoque es ver lo que está tratando de hacer (que está bastante cerca del enlace en using arriba) y crear una corrección lo más simple y eficaz posible.

El problema es que la tubería debe ser una promesa de estilo Bluebird , que si entiendo correctamente, no se adhiere al rendimiento de la promesa nativa (y, por lo tanto, pierde todo el seguimiento + la funcionalidad asincrónica nativa).

Prefiero hacer algo que, bajo el capó, use promesas nativas para las partes asíncronas, pero que brinde la capacidad de vincular el contexto e implementar el uso necesario como disposer .

Para su información, otra cosa que tengo en mente es: en realidad, hay un uso mínimo de usage y .disposer en knex, por lo que tal vez el enfoque funcione mejor para mover eso a un nivel superior.

Vale la pena un experimento :)

oooo también una opción que encontré basada en https://github.com/petkaantonov/bluebird/issues/1593

De cualquier manera, creo que un buen paso adelante fue lo que comenzaste en una rama anterior, donde aislamos todo el uso Promise que en realidad es un BluebirdPromise , de esa manera podemos comenzar a jugar con la eliminación en reemplazos como DisposablePromise o BluebirdNativePromise .

@chaffeqa ¿Te refieres a la parte Bluebird.setScheduler(fn => Promise.resolve().then(fn)) ?
¡La conversión general está procediendo sin problemas! Si pudiéramos mantener a los Eliminadores en Bluebird mientras los hacemos usar promesas nativas debajo del capó, esa podría ser una buena solución.

Solo quería mencionar también que no usar promesas nativas da como resultado nodejs/node#22360 cuando se usa async_hooks lo que hace que se pierda el contexto actual.

La solución es usar el parche https://github.com/TimBeyer/cls-bluebird .

Solo para información, LTS para Node v8 finaliza este año.

@Bessonov ¿Contexto? ¿Cómo afecta este problema subir el nodo mínimo a 10? Tenga en cuenta que ya eliminamos la compatibilidad con el nodo 6.

No estoy familiarizado con la base de código knex, pero tal vez hay algunas características que pueden ayudarlo a alejar a bluebird. Por ejemplo, el nodo 10 tiene soporte para Promise.finally .

Pero de todos modos, estoy feliz de ver el progreso en este tema :+1:

Acerca del patrón de eliminación: ¿podríamos simplemente agregar una devolución de llamada opcional para las cosas, que prometen regresar desechables?
(Al igual que con las transacciones)

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
}

¿Qué nivel de promesa de independencia bibliotecaria necesitamos?
1) todos usan promesas nativas
2) promesas nativas internas, el usuario puede configurar su propia biblioteca de promesas para la interfaz
3) el usuario puede configurar la librería de promesa para los elementos internos y la interfaz

Cuál es el estado actual de este problema. En general, knex funciona ahora con async await, pero TypeScript informará una advertencia de que estamos esperando un método que no es una promesa nativa.

Entonces, para responder a la pregunta del problema original. La solución actual es simplemente esperar y agregar algo como // tslint:disable-next-line: await-promise

@maximelkin Voto por la opción 1. A largo plazo, espero que todas las bibliotecas prometedoras queden obsoletas.

id segundo que, en este punto, estamos más allá de prometer polyfills incluso para la mayoría de los navegadores

@Bessonov actualmente en knex depende de bibliotecas (y tal vez proyectos), lo que requiere exactamente bluebird

deberíamos darles alguna solución alternativa

@Bessonov actualmente en knex depende de bibliotecas (y tal vez proyectos), lo que requiere exactamente bluebird, deberíamos darles alguna solución alternativa

No importa si los usuarios de knex dependen de bluebird. Knex aún puede usar promesas nativas y funcionarán bien con las promesas bluebird. Absolutamente no deberíamos dar ningún respaldo.

Entonces, este problema comenzó con la solicitud de función con la elección de la implementación de la promesa.
De la nada, mutó para eliminar a bluebird sin ningún motivo y romper a todos los dependientes. Sin ninguna advertencia, registro de cambios, opción de respaldo y lanzamiento principal.

Pero supongo que todos los usuarios de 1.5 mecanografiados están contentos ahora.

Entonces, este problema comenzó con la solicitud de función con la elección de la implementación de la promesa.
De la nada, mutó para eliminar a bluebird sin ningún motivo y romper a todos los dependientes. Sin ninguna advertencia, registro de cambios, opción de respaldo y lanzamiento principal.

Al menos antes, las versiones 0.x de knex se han considerado lanzamientos principales con cambios potencialmente importantes, por lo que solo la actualización a 0.20.x debería haberse considerado una actualización segura (semver es realmente flojo cuando el número de versión es < 1).

Eliminar bluebird ha estado sobre la mesa durante mucho tiempo, no se trata solo de este tema.

quitando bluebird sin razon

La eliminación de bluebird no ha sido sin motivo. Todavía puede usar bluebird externamente con promesas knex. Una gran razón para descartar bluebird ha sido que las funciones async implícitamente crean promesas nativas, por lo que, en el futuro, seguir usando Bluebird habría requerido que se agregara un código adicional de envoltura de bluebird en la API de knex sin ningún motivo.

Sin ninguna advertencia, registro de cambios,

Acordado. Recorrí los últimos registros de cambios... Lamentablemente, parece que no hemos enumerado los cambios importantes entre versiones. Debemos ser más cuidadosos al escribir registros de cambios para señalar realmente los cambios, lo que rompe las antiguas API. Por ejemplo, muchos de los cambios de escritura en realidad romperán el código TS antiguo.

Hubo el mismo problema en el proyecto ioredis https://github.com/luin/ioredis/commit/da60b8b. Querían admitir promesas nativas, y los muchachos hicieron una solución realmente buena: agregaron una opción para admitir cualquier biblioteca de promesas personalizada y usan la promesa nativa de forma predeterminada. ¿Por qué no? Establecer una biblioteca de promesa personalizada es rápido y no requiere parchear todo el código de la aplicación.

seguir usando Bluebird habría requerido que se agregara un código adicional de envoltura de bluebird en la API knex sin ningún motivo.

Sip. Pero, ¿por qué no envolver las llamadas de módulo en bluebird (o cualquier otra biblioteca prometedora) si se especificó explícitamente? Es un envoltorio simple, sin gastos generales, y permitiría a los usuarios usar cualquier biblioteca de promesas que deseen. Si nadie necesita bluebird, nadie usaría estas opciones, y puede desaprobarlo con seguridad a tiempo.

Además, vi una opinión que

A largo plazo, espero que todas las bibliotecas prometedoras queden obsoletas.

Pero en mi humilde opinión, hay dos suposiciones incorrectas:

  • Se utiliza Bluebird porque es más rápido.
  • Bluebird se utiliza como pollyfill.

Creo que ese no es el caso de las aplicaciones realmente complejas, que van más allá de las frases asincrónicas y de espera.
Bluebird tiene muchas características que son absolutamente necesarias para un flujo asíncrono complejo, como tiempos de espera, manejo de errores personalizado, mapeo con concurrencia, cancelación, reducción, etc. Todas esas características se pueden implementar en promesas nativas, pero eso es un montón de repeticiones inútiles. En 2020, todavía usamos bluebird en el Nodo 12 porque no queremos todo este modelo.

¿Por qué no? Establecer una biblioteca de promesa personalizada es rápido y no requiere parchear todo el código de la aplicación.

Cualquier cosa que use async-await internamente forzará las promesas a las promesas nativas, por lo que sus opciones son envolver la salida de cada método en la promesa personalizada o prohibir async-await en el código interno. No es una empresa tan pequeña como podría parecer a primera vista.

@qubyte

No es una empresa tan pequeña como podría parecer a primera vista.

No, eso es tan simple como ya te dije. Haces un contenedor para las funciones externas exportadas y eso es todo. Unas 10 líneas de código. Y escriba todo el código interno de la forma que desee.

@jehy : siéntase libre de enviar un PR para esas 10 líneas de código si ve una forma sencilla de implementarlas.

También pasaré algún tiempo hoy tratando de encontrar una solución alternativa.

Por si sirve de algo, gran parte de la API de bluebird se duplica con la misma o una API cercana usando promesas nativas de estos paquetes: https://github.com/sindresorhus/promise-fun

Por lo que vale, gran parte de la API de bluebird se duplica con la misma o una API cercana usando promesas nativas de estos paquetes.

~50 paquetes en lugar de 1? ¿Seriamente?

Sí, aunque la mayoría de las veces solo se requieren unos pocos (p-map, por ejemplo). Su kilometraje puede variar, por supuesto. Se ofrece solo como una ruta potencial hacia lo que desea.

@jehy : aquí hay algo que puede probar como una solución temporal dentro del código de su aplicación:

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();

Esto no es realmente una solución adecuada al problema general. Hay otros objetos temporales creados dentro knex que deberían decorarse de manera similar.

Además, descargo de responsabilidad: la solución alternativa ☝️ ha tenido muy pocas pruebas. Por lo tanto, debe volver a ejecutar las pruebas de su aplicación para asegurarse de que nada haya fallado.

Solo quería agregar mis 2 centavos aquí: realmente aprecio todo el trabajo realizado en esta migración, independientemente de los comentarios negativos.

Desde la perspectiva de nuestra aplicación, knex fue la última biblioteca que nos obligó a requerir Bluebird, y cumplir con el soporte completo de la promesa nativa significa que:

  1. ya no tenemos rastros de pila manchados
  2. redujimos nuestro peso SSR en una cantidad decente
  3. mejoramos el rendimiento ya que la espera asincrónica nativa ahora tiene más rendimiento que bluebird (¡y crece cada vez más!)

es una gran victoria continuar avanzando hacia el estándar es... y sé que no es fácil para los encargados del mantenimiento de la biblioteca, así que quería saludarlos y agradecerles por asumir tal carga.

para aquellos que sufren el cambio: me encantaría ayudar ya que nos hemos beneficiado, así que comuníquese si necesita ayuda para depurar o migrar.

@chaffeqa Gracias por este comentario, ¡significa mucho!

@jehy : ¿Ha tenido la oportunidad de intentar solucionar lo que se propuso? Si es así, ¿resolvió sus problemas inmediatos?

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

mtom55 picture mtom55  ·  3Comentarios

mattgrande picture mattgrande  ·  3Comentarios

mishitpatel picture mishitpatel  ·  3Comentarios

ghost picture ghost  ·  3Comentarios

zettam picture zettam  ·  3Comentarios