Knex: comment configurer pour utiliser la promesse ES6 standard ?

Créé le 22 juil. 2016  ·  80Commentaires  ·  Source: knex/knex

lors de l'exécution avec le nœud v6, existe-t-il un moyen de configurer pour utiliser uniquement la promesse ES6 standard (se débarrasser de la dépendance bluebird) ?

par exemple dans le paquet promise-queue, il utilise par défaut tout ce qui est globalement disponible Promise,

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

ou l'utilisateur peut le configurer explicitement par Queue.configure(require('vow').Promise);

Alors, ce package peut-il mettre en œuvre une stratégie similaire ?

discussion

Commentaire le plus utile

:+1 : pour les promesses natives.

Tous les 80 commentaires

Curieux : vous souhaitez remplacer une bibliothèque plus rapide par une bibliothèque intégrée plus lente ? Sur le client, cela n'aurait pas d'importance, mais la vitesse du nœud est un facteur. Quel est votre raisonnement pour cela ?

@johanneslumpe C'est un facteur dans certaines applications, avec knex je doute fortement que la bibliothèque Promise utilisée ait un effet significatif sur les performances. Il a été discuté que nous devrions écrire tout le code Promise pour utiliser uniquement les API A+ afin que bluebird ne soit pas nécessaire.

Après cela, il devrait être facile de remplacer la bibliothèque Promise utilisée.

Je suis d'accord que cela semble inutile dans une bibliothèque telle que knex. Étant donné que la base de code de knex importe déjà son propre fichier promise.js en interne, il serait techniquement encore très facile à mettre en œuvre, à condition que l'API soit la même _(ce dont je ne suis pas sûr ?)_. Dans ce fichier, on peut utiliser par défaut des promesses globales, sinon nécessiter une bibliothèque configurée, ou quelque chose comme ça.

il s'agit de donner le choix aux utilisateurs ; pour les utilisateurs de Node V6+, donnez le choix de gérer le moins possible les dépendances tierces.

Curieux : vous souhaitez remplacer une bibliothèque plus rapide par une bibliothèque intégrée plus lente ?

ce que vous dites était peut-être vrai dans le passé, mais qu'en est-il maintenant ou 6 mois plus tard ? (J'ai fait quelques recherches et je ne peux trouver que quelques repères de 2015, si vous avez une comparaison plus récente, veuillez poster quelques liens)
Je pense que l'intérêt de faire de Promise la norme ES6 est que les gens puissent facilement utiliser Promise, sans avoir à s'appuyer sur une bibliothèque tierce, et l'équipe principale de Node ou de V8 ne peut pas être aveugle à jamais à la différence de performances, tant que les licences open source des deux projets sont compatibles, ils peuvent même emprunter du code ; ou donnez-leur simplement du temps, je pense que la promesse intégrée peut être plus rapide et meilleure.

voir AWS-SDK d'Amazon : il utilise également par défaut la promesse disponible dans le monde entier ; tout en donnant à l'utilisateur le choix de configurer également la bibliothèque Promise préférée

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

tout est question de choix

À la réflexion, ce ne sera pas aussi simple que de changer un seul fichier. Knex s'appuie actuellement beaucoup sur les fonctions utilitaires Bluebird telles que .timeout , .tap , .reduce , .map et ainsi de suite, qui je suppose et m'attends à ne pas exister dans ES6 Promesses.

Je suis intéressé à soutenir les promesses ES6. Idéalement, nous aurions besoin d'un deuxième argument pour le constructeur Knex qui prend un constructeur Promise . La rétrocompatibilité serait obtenue comme suit :

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

Peut-être pourrions-nous conditionnellement alias map , filter etc. en fonction de leur présence sur Promise.prototype ?

Je pense que cela devrait être assez bas sur la liste des priorités, et cela nécessiterait pas mal de changements internes, sans parler de la diminution des performances (bien qu'il soit vrai que je n'ai pas vu de repères depuis un certain temps) et le fait que les gens pourraient compter sur le fait que les promesses retournées sont un oiseau bleu (pour conditionnel catch , etc).

Je serais plus enclin à attendre que async/wait atterrisse dans un nœud stable pour envisager de résoudre ce problème.

pour les gens qui veulent le minimum de dépendances tierces, la promesse native peut être meilleure,

comme @google-cloud et de nombreuses autres bibliothèques, vous pouvez définir par défaut l'utilisation de Promise native et accepter un paramètre de promesse pour qui souhaite utiliser des bibliothèques tierces

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

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

@tgriesser Je sais que c'est un vieux problème, mais nous avons maintenant async / await à la fois stable et Carbon.

Étant donné qu'il est préférable que async/wait fonctionne avec des promesses natives ( selon la spécification, les fonctions async doivent renvoyer une promesse native ), est-ce une priorité plus élevée ?

Si le support de promesse native est quelque chose que Knex aimerait voir se produire, mais qu'il n'est pas actuellement sur le radar, un PR serait-il le bienvenu ?

Merci pour votre temps.

Les fonctions @malexdev async renvoient des promesses natives qui n'ont pas grand-chose à voir avec knex actuellement. Cela ne signifie pas que await a besoin de promesses natives pour fonctionner correctement. Pourriez-vous préciser dans quel cas il s'agit d'un problème/avantage (sauf de supprimer une dépendance) ?

Cela étant dit, je ne suis pas contre l'abandon de Bluebird, mais il a vraiment besoin de quelques changements internes. Nous devrons peut-être implémenter certaines méthodes actuellement exposées de bluebird aux API knex, à moins que les anciennes fonctionnalités ne soient masquées sous un commutateur de configuration et que des promesses natives soient utilisées par défaut. De cette façon, la migration ne serait pas impossible pour les personnes qui ont relayé le fait que knex renvoie des promesses bluebird .

@elhigu Le principal avantage pour moi personnellement est que j'utilise TypeScript avec une règle TSLint appliquant les promesses natives utilisées pour await , donc je dois envelopper tous les appels Knex dans un Promise.resolve() . Je me rends compte que cela n'a rien à voir avec Knex spécifiquement, et c'est probablement un problème qui m'est propre.

En dehors de cela, à mon avis, il est préférable d'avoir moins de dépendances tierces, et comme @c0b l'a mentionné, plus d'options ne sont jamais une mauvaise chose.

Je me rends compte que ce serait beaucoup de travail, ce qui est l'une des raisons pour lesquelles je suis plus qu'heureux de passer du temps là-dessus, si c'est quelque chose vers lequel Knex est intéressé.

Ouais, j'ai atterri ici à cause d'un problème de dactylographie - j'utilise Knex comme moteur SQL pour ma bibliothèque de stockage de données multi-stockage, et bien que je sois ambivalent vis-à-vis des promesses natives vs bluebird, je ne peux pas utiliser facilement tapuscrit sur knex pour cette raison. Je traite knex thenables comme suivant la spécification native (je n'utilise aucune des extensions bluebird), mais le tapuscrit me dérange à propos du retour d'un Bluebirdlorsque la déclaration de méthode est une promesse.

C'est en quelque sorte à deux niveaux ici, car nous traitons à la fois de la mise en œuvre de la promesse et des typages pour knex (qui sont gérés par différents développeurs), mais je suis essentiellement coincé ici - je romps techniquement le contrat de type par retourner un Bluebird quand j'ai déclaré une Promise (je suppose qu'il y a des choses dans l'API Promise que Bluebird ne prend pas en charge ?) mais je n'ai vraiment pas envie de mettre un tas de return Promise.resolve(bluebirdthing) partout non plus.

J'ai passé assez de temps à creuser dans les tripes de knex au cours de l'année écoulée et à travailler avec des promesses en général que je serais prêt à prendre quelque chose ici et à travailler sur un PR pour modulariser la mise en œuvre de la promesse si les gens le souhaitent - seriez-vous ouvert à un PR? Cela finirait par ressembler à ce que @elhigu a mentionné - réimplémenter certaines des fonctions utilitaires pour utiliser le constructeur Promise transmis à l'instanciation afin d'éviter les besoins de réécriture de code. Pas sûr des performances, bien sûr, mais c'est quelque chose qui peut être comparé.

Avoir tout fait avec async / await serait cool aussi, et je ne suis pas pressé de régler ce problème (en fin de compte, pour mon cas d'utilisation, je finis par signaler les choses comme any et traiter avec ceux codebranches comme s'il s'agissait de javascript au lieu de tapuscrit).

@ericeslinger Je ne vois pas pourquoi la véritable implémentation de Promise utilisée causerait des problèmes avec TypeScript, j'ai mélangé des promesses bluebird et natives pendant un an et demi sans aucun problème ...

Je n'ai tout simplement pas utilisé de typage qui introduirait des types pour bluebird, je dis juste aux typages tapuscrites qu'il s'agit de promesses normales et cela ne voit aucune différence (ofc. il se plaindra si j'essaie d'utiliser les méthodes spéciales de bluebird).

Quelle version de dactylographie et existe-t-il un exemple de projet à reproduire ... par exemple github repo avec le script de démarrage npm.

C'est en quelque sorte à deux niveaux ici, car nous traitons à la fois de la mise en œuvre de la promesse et des typages pour knex (qui sont gérés par différents développeurs), mais je suis essentiellement coincé ici - je romps techniquement le contrat de type par retourner un Bluebird quand j'ai déclaré une Promise (je suppose qu'il y a des choses dans l'API Promise que Bluebird ne prend pas en charge ?) mais je n'ai vraiment pas envie de mettre un tas de retour Promise.resolve(bluebirdthing) partout non plus.

Ces typages knex proviennent-ils de npm ? Les typages bluebird proviennent-ils de npm ? Quels forfaits ? J'ai du mal à comprendre pourquoi cela arriverait. S'il existe un type Bluebird distinct, il doit être hérité de la promesse native et pouvoir revenir des API qui indiquent qu'elles renverront Promise. D'après la description, il semble que le problème soit des implémentations de typage très cassées. Et ce n'est pas pertinent pour ce problème (le tapuscrit ne se soucie pas des types réels, il ne saura donc pas ce que knex renvoie).

Je reçois mes saisies du référentiel DefinitelyTyped via l'installation @types/knex . Cette définition tire avec elle @types/bluebird , et toutes les méthodes knex sont typées comme retournant des objets Bluebird.

En tant que chose spécifique, je ne peux pas faire ceci:

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

en utilisant ces typages. C'est parce que knex.select().then() est tapé pour renvoyer un Bluebird dans le référentiel DefinitelyTyped, et ceux-ci s'enchaînent pour créer plus de Bluebirds, et disent quelque chose comme return foo as Promise<any>() quand foo est un Bluebirdéchouera (du moins, il échoue dans le dactylographie 2.4), car les Bluebirds ne sont pas assignables aux promesses (ils manquent de [Symbol.toStringTag] selon this , donc contraindre l'un à l'autre serait une erreur, bien qu'une petite erreur).

Au lieu de cela, je peux changer pour

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

ou faites d'autres astuces pour envelopper tous les appels à knex dans un appel natif Promise.resolve(). Cela empêchera le script de se plaindre en aval des fonctions de ma bibliothèque, tout en me permettant d'utiliser les typages knex dans les fonctions de ma bibliothèque.

Avant hier, je n'avais pas du tout utilisé @types/knex - je tapais juste knex comme un any . Le code fonctionne bien de toute façon à l'exécution (pour mon cas d'utilisation au moins), c'est juste

@elhigu : Le problème n'est pas les implémentations de typage cassées.
TypeScript définit le type des fonctions async comme Promise<[type]> , ce qui est correct selon les spécifications JS.
Knex renvoie Bluebird<[type]> , ce que les typages reflètent avec précision.

Je n'ai tout simplement pas utilisé de typages qui introduiraient des types pour bluebird, je dis juste aux typages tapuscrites qu'elles sont des promesses normales et cela ne voit aucune différence

Cela ment au compilateur, car les fonctions Knex renvoient en fait Bluebird s. Pas intéressé.
Vous avez raison de dire que les Bluebirds sont compatibles avec Promises, mais une partie de l'accord avec TypeScript est que vous renvoyez réellement ce que vous dites que vous renvoyez.

Lors du retour d'un Bluebird partir d'une fonction qui a été tapée pour retourner Promise , TypeScript se plaint car le type Bluebird n'est pas le même que le type Promise .
Il existe diverses astuces que nous pouvons faire (comme ce que @ericeslinger a mentionné à propos de l'utilisation any ou de l'emballage dans Promise.resolve() ) mais en fin de compte, des astuces comme celle-ci nous font perdre une grande partie de ce que TypeScript fournit.

En fin de compte, la réalité est qu'il y a au moins deux utilisateurs maintenant qui disent "L'utilisation de promesses natives est importante pour nous, et nous sommes prêts à travailler pour rendre la fonctionnalité de promesse plus générique".

Je me rends compte que vous essayez juste d'aider, mais franchement au lieu d'entendre "vous pourriez le faire de cette façon", j'aimerais savoir si les changements de promesse proposés par moi-même / @ericeslinger / @c0b sont acceptables pour que je puisse soit commencer sur un PR ou quoi.

@malexdev @ericeslinger Merci pour plus d'infos ! On dirait qu'il n'est en fait pas possible d'hériter de votre propre classe de Promise , ce qui pourrait être la raison pour laquelle le retour de Bluebirds à partir d'une fonction qui est typée pour être Promise<> échoue :(

@ericeslinger Quoi qu'il en soit, ce n'est pas un problème lorsque vous créez des fonctions async , car elles encapsulent automatiquement les résultats dans les promesses natives en interne. La suite est conforme sans problème, avec les typages de @types/bluebird et compilée vers 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);
});

sortir:

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

Donc, pour l'instant, lorsque vous utilisez les API knex, vous devez en fait indiquer que vous renvoyez Bluebird à moins que vous n'utilisiez des fonctions/méthodes asynchrones, ce qui encapsule automatiquement bluebird dans les promesses natives.

@malexdev

Cela ment au compilateur, car les fonctions Knex renvoient en fait des Bluebirds. Pas intéressé.
Vous avez raison de dire que les Bluebirds sont compatibles avec Promises, mais une partie de l'accord avec TypeScript est que vous renvoyez réellement ce que vous dites que vous renvoyez.

En fait, traiter avec le tapuscrit, c'est qu'il suffit que l'objet renvoyé implémente correctement l'interface, par exemple c'est parfaitement 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;
}

Cela gâche instanceof, mais le script dactylographié n'a même pas promis que vous avez renvoyé l'instance native de Promise, uniquement l'interface.

Lors du renvoi d'un Bluebird à partir d'une fonction qui a été tapée pour renvoyer Promise, TypeScript se plaint car le type Bluebird n'est pas le même que le type Promise.
Il existe diverses astuces que nous pouvons faire (comme ce que @ericeslinger a mentionné à propos de l'utilisation de n'importe lequel ou de l'emballage dans Promise.resolve()), mais en fin de compte, des astuces comme celle-ci nous font perdre une grande partie de ce que TypeScript fournit.

Je détesterais voir des gens devoir faire ce genre de trucs / changer les implémentations JS juste pour satisfaire les mauvaises frappes.

En fin de compte, la réalité est qu'il y a au moins deux utilisateurs maintenant qui disent "L'utilisation de promesses natives est importante pour nous, et nous sommes prêts à travailler pour rendre la fonctionnalité de promesse plus générique".

Je me rends compte que vous essayez juste d'aider, mais franchement au lieu d'entendre "vous pourriez le faire de cette façon", j'aimerais savoir si les changements de promesse proposés par moi-même / @ericeslinger / @c0b sont acceptables pour que je puisse soit commencer sur un PR ou quoi.

Merci de votre compréhension :) La modification de knex pour utiliser les promesses natives a déjà été lancée et mise en œuvre jusqu'à un certain point l'année dernière, puis elle a été modifiée par @tgriesser , donc je dirais que pour l'instant, il vaut mieux ne pas commencer ce changement.

De plus, je considère toujours ces problèmes de dactylographie mentionnés dans ce fil comme des problèmes de déclaration de typage (pourquoi bluebird n'implémente pas Promise correctement ... je dois approfondir cela?), que des problèmes d'implémentation de knex.

Cela étant dit, je ne suis pas opposé à l'élimination de l'oiseau bleu dans un certain laps de temps en voyant simplement deux problèmes distincts ici.

:+1 : pour les promesses natives.

@elhigu :

En fait, traiter avec le tapuscrit, c'est qu'il suffit que l'objet renvoyé implémente correctement l'interface

Assez juste. Je maintiens toujours mon opinion selon laquelle moins de dépendances et plus de choix, c'est mieux, mais je vois maintenant ce que vous vouliez dire à propos des dactylographies cassées.

Donc toujours 👍 pour les promesses natives (pour lesquelles je suis toujours prêt à aider), mais je vois maintenant que mon problème immédiat peut être résolu en corrigeant les typages Bluebird. Merci pour l'info.

J'ai donc commencé à utiliser un peu TypeScript et je l'aime à la fois et je réalise maintenant que les problèmes ici sont un véritable problème. Alors que async/wait prend de plus en plus d'importance dans les récents nœuds, l'utilitaire Bluebird fns ( map , reduce , tap , bind , return ) deviennent moins utiles. Je serais d'accord pour continuer à utiliser Bluebird en interne, mais officiellement "déprécier" l'API publique du générateur de requêtes knex renvoyant toutes les méthodes utilitaires supplémentaires.

Avec cela fusionné, nous pourrions alors mettre à niveau les définitions TypeScript pour supprimer les typages Bluebird (sauf pour toBluebird ) et changer les typages Bluebird en typages Promise.

Si quelqu'un a de la bande passante veut s'attaquer à ce problème, voici ce que je pense comme plan d'action :

  • [ ] Ajoutez un avertissement de dépréciation pour toutes les méthodes proxy Bluebird ( tap , map , reduce , bind , return ).
  • [ ] Ajoutez une méthode .toBluebird() qui sera un chemin de migration pour ceux qui veulent continuer à utiliser Bluebird (ils peuvent le faire avec une simple recherche/remplacement de tous les appels des méthodes ci-dessus et ajoutez-la simplement avant ceux-ci s'appellent)
  • [ ] Une fois que cela est fusionné/nouvelle version coupée (0.15), nous pouvons mettre à jour les définitions de Typescript
  • [ ] Finalement, nous pouvons abandonner complètement ces méthodes. Cette API plus simple ouvre la voie à l'utilisation éventuelle de Promises natives et async/wait quand cela a du sens.

Faites-moi savoir si cela a du sens et si quelqu'un est intéressé à tenter sa chance.

Serait certainement intéressé à aider avec cela.

Cela signifie-t-il que Knex commencerait à gérer ses propres définitions TypeScript ? Cela nous permettrait de faire des choses intéressantes avec des génériques que les typages générés automatiquement ne prendront jamais en charge.

J'ai commencé ce fork comme une première tentative pour ajouter la prise en charge des promesses natives :
https://github.com/tgriesser/knex/pull/2523/files

Comme ce commentaire de 2016 :

Curieux : vous souhaitez remplacer une bibliothèque plus rapide par une bibliothèque intégrée plus lente

Incroyable à quel point cela peut changer en 2 ans.

Lors du renvoi d'un Bluebird à partir d'une fonction qui a été tapée pour renvoyer Promise, TypeScript se plaint car le type Bluebird n'est pas le même que le type Promise.

@malexdev En fait, le script typographique utilise le typage structurel (le flux utilise le typage nominal et fonctionnerait comme vous le décrivez) donc tant que votre type remplit l'interface Promise il est compatible avec Promise s'il est explicitement extends / implements ou pas.

Comment ça évolue ? Je pense qu'une bonne première étape serait de factoriser les appels de méthode spécifiques à Bluebird dans knex (c'est-à-dire de ne pas encore le supprimer). La suppression de bluebird et la fourniture d'une option pour un constructeur Promise personnalisé suivraient (et donneraient aux utilisateurs des méthodes Bluebird un chemin de mise à niveau).

Je ne commence pas à travailler sur la première étape s'il n'y a pas d'objection. Les travaux existants semblent s'être éteints.

@qubyte Je ne pense pas qu'il y ait un effort actif pour faire le changement, des changements incrémentiels ont été apportés ici et là, mais c'est à peu près tout.

D'accord. Dans mon prochain temps libre, je ferai quelques changements aussi petits que possible pour factoriser chaque méthode.

@tgriesser Des avis quand devrions-nous aller de l'avant avec celui-ci (si jamais) ? Pour moi, avril prochain semblerait être un délai raisonnable lorsque Node 6 LTS atteindra la fin de la ligne.

Informations intéressantes en 2018 :

promises-native-async-await a de meilleures performances que promises-bluebird dans le nœud 10.
Référence : https://github.com/petkaantonov/bluebird/tree/master/benchmark

La performance n'est donc plus une raison pour garder Bluebird. Nous devrions opter pour async/wait.

promises-native-async-wait a de meilleures performances

c'est aussi ce que je croyais fermement en 2016 que la méthode native s'améliorerait beaucoup plus rapidement, simplement parce que c'est le cœur de la communauté Nodejs, a plus de personnes qui s'en soucient, plus que n'importe quelle bibliothèque tierce

Bien que le ticket ait été déposé demandant des choix, il y a tellement d'implémentations concurrentes Promise , ce n'est tout simplement pas bon de parier sur bluebird pour toujours

Y a-t-il une mise à jour à ce sujet ?

@cfanoulis Le même tient toujours. Quand avril arrivera, nous pourrons abandonner la prise en charge de Node 6 et commencer à supprimer bluebird.

une mise à jour 2019 ? /cc à certains contributeurs ou mainteneurs principaux ou à toute personne @here qui s'en soucie : @johanneslumpe @tgriesser @wubzz @elhigu de https://github.com/tgriesser/knex/graphs/contributors?type=c&from=2018-01- 01&à=2019-12-31

D'un autre côté, la communauté JavaScript est un monde tellement dynamique, vibrant et parfois cruel, tous les 3 ou 2 ans (ou même plus vite) il y a des remplacements pour quelque chose que nous connaissions auparavant, pensez à Grunt, Gulp => Webpack, les outils, bibliothèques, les frameworks sont pleinement en concurrence à tous les niveaux, donc, aux bibliothèques plus anciennes si vous arrêtez d'apporter des innovations ou si vous ralentissez la prise en charge de nouvelles normes (pensez aux itérateurs async/wait ES2019 ...) vous serez éventuellement remplacé

Je viens de faire quelques recherches simples, il semble qu'au niveau DB ORM il existe également de nombreuses alternatives, TypeORM pourrait être une bonne ... (je m'arrête ici pour ne pas en dire plus ...)
https://bestofjs.org/tags/db
https://bestofjs.org/projects/typeorm

@c0b pas besoin de cc. Je reçois des mails de tous les commentaires de toute façon. @kibertoad vient de dire tout ce qui devait être dit dans son dernier commentaire ... Ce problème n'a également rien à voir avec knex supportant mieux les fonctionnalités async/wait ES2019 et knex n'est pas un ORM donc je ne suis pas sûr de ce que ce commentaire était vraiment sur.

Si vous avez besoin d'un bon ORM, je peux vous recommander objection.js. Il est également implémenté au-dessus de knex, voici un joli fil de discussion à ce sujet https://github.com/Vincit/objection.js/issues/1069

À un moment donné, ce knex sera remplacé, mais pas par un ORM. Il pourrait être remplacé par un autre générateur de requêtes, qui a une base de code plus propre et une API plus cohérente. Comme par exemple knex 1.0 peut-être ;)

De plus, si knex est remplacé, je serais tout à fait d'accord avec ça, moins de travail pour moi :D

Il y a WIP là dessus je crois : https://github.com/tgriesser/knex-next

Je voulais juste mentionner également que ne pas utiliser les promesses natives entraîne https://github.com/nodejs/node/issues/22360 lors de l'utilisation async_hooks , ce qui entraîne la perte du contexte actuel.

Croyez-moi, nous n'avons pas besoin de raisons supplémentaires pour déménager, nous voulons le faire aussi mal que vous tous :). Cependant, nous devons encore publier quelques correctifs supplémentaires pour la branche Node 6, puis (enfin), nous l'abandonnerons et commencerons à supprimer progressivement bluebird.

Après la fusion de #3227, nous pouvons enfin commencer !

Je sais que vous avez mentionné précédemment que vous pourriez utiliser de l'aide dans cette migration, si c'est toujours la direction que vous voulez prendre, pourrions-nous vous aider de quelque manière que ce soit ?

Je pense : créer un projet, ajouter quelques tâches et voir si quelqu'un (peut-être que j'ai le temps) pourrait être affecté et fixer des dates ?

@chaffeqa créera bientôt des tâches plus granulaires, préparez # 3250 pour une première série de changements faciles. Nous devons principalement remplacer les utilisations de bluebird.tap, bluebird.method et bluebird.try par quelque chose de natif. Si vous avez déjà un peu de temps, vous pouvez essayer de bifurquer # 3250 et de jeter un coup d'œil à tous les besoins restants de 'bluebird' (je recommanderais de commencer par ceux qui ne sont pas spécifiques à un dialecte afin que vous puissiez rapidement valider les fonctionnalités qui fonctionnent toujours en exécutant test:sqlite sans aucune configuration Docker).

@qubyte Si vous souhaitez contribuer, c'est le moment !

@kibertoad suis-je sûr d'utiliser async/wait maintenant ?

Vous voulez dire dans la base de code knex ? Sûr. Dans la tienne tu l'as toujours été :-D

Désolé d'avoir été MIA la semaine dernière, les choses s'accélèrent pour notre entreprise, je dois donc me concentrer là-dessus.

Je voulais boucler la boucle sur certaines des discussions que nous avons eues sur l'un des plus gros blocs de la mise à niveau : le remplacement de l'utilisation Disposer .

C'est un trou assez profond lorsque vous commencez à le descendre, il faudra donc une bonne ingénierie pour fournir une bonne copie / abstraction. Je crains que la surcharge de performance de quelque chose ne soit assez importante (beaucoup plus d'objets créés à mesure que la chaîne de promesses se développe).

En fait, j'ai commencé sur quelques POC, et je pense que c'est le plus simple d'entre eux :

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

Et un autre:

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

Mais je n'ai pas eu le temps de les prouver.

Je pense qu'il peut être intéressant d'explorer d'autres options (garder bluebird mais le convertir pour utiliser les promesses natives en interne ?) En raison du fait que cette fonctionnalité doit être dans le référentiel (à moins que vous ne puissiez penser à de meilleures approches ... des itérateurs asynchrones? J'adorerais entendre les réflexions de l'équipe de bluebird, même sur l'abstraction de cette fonctionnalité, même si mon instinct dit que c'est assez lié aux crochets d'implémentation de bluebird.

Je dirais que si nous pouvons comprendre cette partie, le reste de ces tâches est assez simple.

@chaffeqa Np, merci de trouver encore le temps d'y revenir !
Je doute fortement que les gens de Bluebird soient ouverts aux suggestions pour réorganiser sérieusement leur implémentation, ils ont répété à plusieurs reprises qu'à ce stade, ils sont intéressés par la stabilité avant tout, et ils recommandent aux gens d'utiliser réellement les promesses natives à moins que l'on ait vraiment besoin de fonctionnalités avancées fourni par Bluebird.
Considérant que Node 8 semble être la version la plus populaire de Node.js en ce moment (sur la base des statistiques officielles de téléchargement de Node.js), je crains que nous ne puissions pas encore passer à une approche basée sur un itérateur asynchrone.
Quels inconvénients voyez-vous à Knex implémentant DisposablePromise en interne ? Puisqu'il étend la promesse native, je suppose qu'il n'apporte aucun des inconvénients de Bluebird, et rien dans l'espace utilisateur n'a besoin de le savoir?

@ericeslinger FWIW, les typages TS ne devraient plus être un problème dans master, nous tapons maintenant nos promesses en tant que natifs pour décourager les utilisateurs de se fier aux fonctionnalités de Bluebird. Cela peut causer des problèmes plus tard lorsque les promesses natives implémentent quelque chose que les promesses Bluebird ne font pas, donc nous voulons toujours remplacer les promesses utilisées autant que possible. Toute contribution dans ce sens serait grandement appréciée :)

Noix j'ai pensé autant 😞
Je suis d'accord que faire quelque chose comme le DisposablePromise est probablement la voie à suivre dans ce cas, d'autant plus que l'élément qui est vraiment nécessaire est toujours en proposition .

L'inconvénient est qu'il va être très important de concevoir quelque chose comme DisposablePromise de manière judicieuse... et franchement, je ne sais même pas si mon implémentation fonctionne 😆 (j'ai tellement de mal à penser asynchrone pour une raison quelconque Ha).

S'il y a quelqu'un d'autre sur ce fil qui aimerait essayer de résoudre ce problème <3 u longtimes !

@chaffeqa À quel point la mise en œuvre de Bluebird est-elle compliquée ? Peut-être pouvons-nous simplement l'extraire et l'ajouter en plus de la promesse native ?

@chaffeqa Dans le pire des cas, nous pouvons supprimer toutes les autres utilisations de Bluebird et conserver celle-ci en raison de sa complexité si nous la jugeons trop risquée. Pas idéal, mais finalement using va arriver.

malheureusement assez compliqué ... l'implémentation se greffe sur le fait que bluebird contrôle le cycle de vie des promesses. Je pense que la meilleure approche est de voir ce qu'il essaie de faire (ce qui est assez proche du lien sur using ci-dessus) et de créer un shim aussi simple et performant que possible.

Le problème est que le pipeline doit être une promesse de style Bluebird , qui si je comprends bien, n'adhère pas aux performances de la promesse native (et vous perdez donc toutes les fonctionnalités de traçage + asynchrone native).

Je préférerais de loin faire quelque chose qui utilise sous le capot des promesses natives pour les parties asynchrones, mais offre la possibilité de lier le contexte et d'implémenter l'utilisation nécessaire comme un disposer .

Pour votre information, une autre chose dans mon esprit est la suivante : il y a en fait une utilisation minimale de usage et .disposer dans knex, alors peut-être que l'approche fonctionne mieux pour déplacer cela à un niveau supérieur ?

Ça vaut le coup d'essayer :)

oooo aussi une option que j'ai trouvée basée sur https://github.com/petkaantonov/bluebird/issues/1593

Quoi qu'il en soit, je pense qu'un bon pas en avant a été ce que vous avez commencé sur une branche précédente, où nous isolons toute l'utilisation Promise qui est en fait un BluebirdPromise , de cette façon nous pouvons commencer à jouer avec la suppression en remplacement comme DisposablePromise ou BluebirdNativePromise .

@chaffeqa Vous voulez dire la partie Bluebird.setScheduler(fn => Promise.resolve().then(fn)) ?
La conversion globale se déroule très bien ! Si nous pouvions garder Disposers dans Bluebird tout en leur faisant utiliser des promesses natives sous le capot, cela pourrait en fait être une bonne solution.

Je voulais juste mentionner également que ne pas utiliser les promesses natives entraîne nodejs/node#22360 lors de l'utilisation async_hooks , ce qui entraîne la perte du contexte actuel.

La solution consiste à utiliser le correctif https://github.com/TimBeyer/cls-bluebird .

Juste pour information, LTS pour Node v8 se termine cette année.

@Bessonov Contexte ? Comment le déplacement du nœud min à 10 affecte-t-il ce problème ? Notez que nous avons déjà abandonné la prise en charge du nœud 6.

Je ne connais pas la base de code knex, mais il existe peut-être certaines fonctionnalités qui peuvent vous aider à vous éloigner de bluebird. Par exemple, le nœud 10 prend en charge Promise.finally .

Mais quoi qu'il en soit, je suis content de voir l'avancée de ce sujet :+1:

À propos du modèle de broyeur - pourrions-nous simplement ajouter un rappel facultatif pour les choses, qui renvoient la promesse jetable ?
(Comme pour les transactions)

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
}

De quel niveau d'indépendance des bibliothèques promises avons-nous besoin ?
1) tous utilisent des promesses natives
2) promesses natives internes, l'utilisateur peut définir sa propre bibliothèque de promesses pour l'interface
3) l'utilisateur peut définir la bibliothèque de promesses pour les composants internes et l'interface

Quel est l'état actuel de ce problème. Généralement, knex fonctionne maintenant avec l'attente asynchrone, mais le script dactylographié signalera un avertissement indiquant que nous attendons une méthode qui n'est pas une promesse native.

Donc, pour répondre à la question du problème initial. La solution de contournement actuelle consiste simplement à attendre et à ajouter quelque chose comme // tslint:disable-next-line: await-promise

@maximelkin Je vote pour l'option 1. À long terme, j'espère que chaque bibliothèque promise sera obsolète.

id deuxième que, à ce stade, nous sommes au-delà des polyfills promis, même pour la majorité des navigateurs

@Bessonov actuellement sur knex dépend des bibliothèques (et peut-être des projets), ce qui nécessite exactement bluebird

nous devrions leur donner une solution de repli

@Bessonov actuellement sur knex dépend des bibliothèques (et peut-être des projets), ce qui nécessite exactement bluebird, nous devrions leur donner une solution de repli

Peu importe si les utilisateurs de knex dépendent de bluebird. Knex peut toujours utiliser des promesses natives et elles interagiront très bien avec les promesses bluebird. Nous ne devons absolument pas donner de repli.

Donc, ce problème a commencé avec la demande de fonctionnalité avec le choix de l'implémentation de la promesse.
Sorti de nulle part, il a muté en supprimant Bluebird sans raison et en brisant toutes les personnes à charge. Sans aucun avertissement, journal des modifications, option de repli et version majeure.

Mais je suppose que tous les utilisateurs de tapuscrit 1.5 sont satisfaits maintenant.

Donc, ce problème a commencé avec la demande de fonctionnalité avec le choix de l'implémentation de la promesse.
Sorti de nulle part, il a muté en supprimant Bluebird sans raison et en brisant toutes les personnes à charge. Sans aucun avertissement, journal des modifications, option de repli et version majeure.

Au moins plus tôt avec knex, les versions 0.x ont été considérées comme des versions majeures avec des changements potentiellement cassants, donc seule la mise à jour vers 0.20.x aurait dû être considérée comme une mise à niveau sûre (semver est vraiment lâche lorsque le numéro de version < 1).

La suppression de Bluebird est sur la table depuis longtemps, il ne s'agit pas seulement de cette question.

enlever bluebird sans raison

La suppression de Bluebird n'a pas été sans raison. Vous pouvez toujours utiliser en externe bluebird avec des promesses knex. L'une des principales raisons d'abandonner bluebird est que les fonctions async créent implicitement des promesses natives, donc à l'avenir, continuer à utiliser Bluebird aurait nécessité l'ajout de code d'emballage bluebird supplémentaire dans l'API knex sans aucune raison.

Sans aucun avertissement, changelog,

D'accord. J'ai parcouru les derniers journaux des modifications... Malheureusement, il semble vraiment que nous n'ayons pas répertorié les changements de rupture entre les versions. Nous devons être plus prudents lors de l'écriture des journaux des modifications pour vraiment signaler les changements, ce qui casse les anciennes API. Par exemple, de nombreux changements de typage vont en fait casser l'ancien code TS.

Il y avait le même problème sur le projet ioredis https://github.com/luin/ioredis/commit/da60b8b. Ils voulaient prendre en charge les promesses natives - et les gars ont fait une très bonne solution - ils ont ajouté une option pour prendre en charge n'importe quelle bibliothèque de promesses personnalisées et ils utilisent la promesse native par défaut. Pourquoi pas? La configuration d'une bibliothèque de promesses personnalisées est rapide et ne nécessite pas de patcher tout le code de l'application.

continuer à utiliser Bluebird aurait nécessité l'ajout d'un code d'emballage bluebird supplémentaire dans l'API knex sans aucune raison.

Ouais. Mais pourquoi ne pas envelopper les appels de module dans bluebird (ou toute autre bibliothèque promise) si cela a été explicitement spécifié ? C'est un wrapper simple, sans frais généraux, et cela permettrait aux utilisateurs d'utiliser la bibliothèque de promesses de leur choix. Si personne n'a besoin de bluebird, personne n'utilisera cette option, et vous pouvez la déprécier en toute sécurité à temps.

Aussi, j'ai vu une opinion qui

À long terme, j'espère que chaque bibliothèque promise sera obsolète.

Mais à mon humble avis, il y a deux hypothèses erronées :

  • Bluebird est utilisé car il est plus rapide.
  • Bluebird est utilisé comme pollyfill.

Je pense que ce n'est pas le cas pour les applications vraiment complexes, qui vont au-delà de l'async-wait one liners.
Bluebird possède de nombreuses fonctionnalités qui sont absolument nécessaires pour un flux asynchrone complexe - comme les délais d'attente, la gestion personnalisée des erreurs, le mappage avec simultanéité, l'annulation, la réduction, etc. Toutes ces fonctionnalités peuvent être implémentées dans des promesses natives, mais c'est beaucoup de passe-partout inutile. En 2020, nous utilisons toujours bluebird dans le nœud 12 car nous ne voulons pas de tout ce passe-partout.

Pourquoi pas? La configuration d'une bibliothèque de promesses personnalisées est rapide et ne nécessite pas de patcher tout le code de l'application.

Tout ce qui utilise async-wait en interne va contraindre les promesses aux promesses natives, de sorte que vos options deviennent soit l'emballage de la sortie de chaque méthode dans la promesse personnalisée, soit l'interdiction de l'async-wait dans le code interne. Ce n'est pas une entreprise aussi petite que cela puisse paraître à première vue.

@qubyte

Ce n'est pas une entreprise aussi petite que cela puisse paraître à première vue.

Non, c'est aussi simple que je l'ai déjà dit. Vous créez un wrapper pour les fonctions externes exportées et c'est tout. Environ 10 lignes de code. Et écrivez tout le code interne comme vous le souhaitez.

@jehy : N'hésitez pas à soumettre un PR pour ces 10 lignes de code si vous voyez un moyen simple de les implémenter.

Je vais également passer un peu de temps aujourd'hui à essayer de trouver une solution de contournement.

Pour ce que ça vaut, une grande partie de l'API de bluebird est dupliquée avec la même API ou une API proche en utilisant des promesses natives par ces packages : https://github.com/sindresorhus/promise-fun

Pour ce que ça vaut, une grande partie de l'API de bluebird est dupliquée avec la même API ou une API proche en utilisant des promesses natives par ces packages

~50 colis au lieu de 1 ? Sérieusement?

Oui, bien que la plupart du temps seuls quelques-uns soient nécessaires (p-map par exemple). Votre kilométrage peut varier bien sûr. Il n'est proposé que comme un itinéraire potentiel vers ce que vous voulez.

@jehy : Voici quelque chose que vous pouvez essayer comme solution de contournement temporaire dans votre code d'application :

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

Ce n'est pas vraiment une solution adéquate au problème global. Il existe d'autres objets temporaires créés dans knex qui devraient être décorés de la même manière.

De plus, avis de non-responsabilité : la solution de contournement ☝️ a été très peu testée. Vous devez donc relancer les tests de votre application pour vous assurer que rien ne s'est cassé.

Je voulais juste ajouter mes 2 cents ici : j'apprécie vraiment tout le travail investi dans cette migration, quels que soient les retours négatifs.

Du point de vue de notre application, knex était la dernière bibliothèque nous obligeant à exiger Bluebird, et se conformer à la prise en charge complète de la promesse native signifie que :

  1. nous n'avons plus de traces de piles salies
  2. nous avons réduit notre poids SSR d'une quantité décente
  3. nous avons amélioré les performances car l'attente asynchrone native est désormais plus performante que bluebird (et croît de plus en plus !)

c'est une si grande victoire de continuer à courir vers la norme es... et je sais que ce n'est pas facile pour les responsables de la bibliothèque, alors je voulais vous crier dessus et vous remercier d'avoir assumé un tel fardeau !

pour ceux qui souffrent du changement : j'aimerais beaucoup aider puisque nous en avons bénéficié, alors n'hésitez pas à me contacter si vous avez besoin d'aide pour le débogage ou la migration !

@chaffeqa Merci pour ce retour, ça veut dire beaucoup !

@jehy : Avez-vous eu l'occasion d'essayer de contourner ce qui a été proposé ? Si oui, cela a-t-il résolu vos problèmes immédiats ?

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

arconus picture arconus  ·  3Commentaires

hyperh picture hyperh  ·  3Commentaires

nklhrstv picture nklhrstv  ·  3Commentaires

rarkins picture rarkins  ·  3Commentaires

koskimas picture koskimas  ·  3Commentaires