Knex: 標準のES6Promiseを使用するように構成する方法は?

作成日 2016年07月22日  ·  80コメント  ·  ソース: knex/knex

ノードv6で実行している場合、標準のES6 Promiseのみを使用するように構成する方法はありますか(bluebirdの依存関係を取り除きます)?

たとえば、promise-queueパッケージでは、デフォルトで、グローバルに利用可能なPromiseを使用します。

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

または、ユーザーはQueue.configure(require('vow').Promise);で明示的に構成できます

では、このパッケージは同様の戦略を実装できますか?

discussion

最も参考になるコメント

:+1:ネイティブPromiseの場合。

全てのコメント80件

好奇心が強い:より速いライブラリをより遅いビルトインに置き換えたいですか? クライアントでは問題ではありませんが、ノードの速度が要因になります。 これについてのあなたの理由は何ですか?

@johanneslumpeこれは、 knexを使用する一部のアプリケーションの要因です。使用しPromiseライブラリがパフォーマンスに大きな影響を与えるかどうかは非常に疑わしいです。 bluebirdが不要になるように、すべてのPromiseコードをA+APIのみを使用するように作成する必要があることが議論されています。

その後、どのPromiseライブラリが使用されているかを簡単に上書きできるはずです。

knexのようなライブラリでは無意味だと思うことに同意します。 knexのコードベースがすでに独自のpromise.jsファイルを内部にインポートしていることを考えると、APIが同じである場合は技術的には非常に簡単に実装できます_(それが正しいかどうかはわかりませんか?)_。 このファイルでは、デフォルトでグローバルプロミスを使用できます。それ以外の場合は、構成済みのライブラリなどが必要です。

それはすべて、ユーザーに選択肢を与えることです。 Node V6 +ユーザーの場合、サードパーティの依存関係を可能な限り最小限に管理する選択肢を提供します。

好奇心が強い:より速いライブラリをより遅いビルトインに置き換えたいですか?

あなたが言っていることは過去に真実かもしれませんが、今または6ヶ月後はどうですか? (私はいくつかの検索を行いましたが、2015年のベンチマークしか見つけることができません。最近の比較がある場合は、いくつかのリンクを投稿してください)
PromiseをES6標準にすることの全体的なポイントは、サードパーティのライブラリに依存することなく、人々がPromiseを簡単に使用できることであり、NodeまたはV8のコアチームは、パフォーマンスの違いを永遠に知らないことはできないと思います。 2つのプロジェクトのオープンソースライセンスには互換性があり、コードを借りることもできます。 または、少し時間を与えてください。組み込みのPromiseがより速く、より良くなると信じています。

AmazonのAWS-SDKを参照してください。デフォルトでは、世界中で利用可能なPromiseを使用します。 ユーザーにお気に入りのPromiseライブラリを構成する選択肢を提供します

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

選択がすべてです

考え直してみると、1つのファイルを変更するほど簡単ではありません。 Knexは現在、 .timeout.tap.reduce.mapなどのBluebirdユーティリティ関数に大きく依存しています。 ES6Promisesで。

ES6の約束をサポートすることに興味があります。 理想的には、 Promise #$コンストラクターを取るKnexコンストラクターへの2番目の引数が必要です。 下位互換性は次のように実現されます。

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

おそらく、提供されたPromise.prototypeでの存在に基づいて、条件付きでmapfilterなどをエイリアスできますか?

これは優先順位リストではかなり低いはずであり、パフォーマンスの低下(確かにしばらくの間ベンチマークを見ていませんが)や人々が依存している可能性があるという事実は言うまでもなく、かなりの内部変更が必要になると思います返されたpromiseがブルーバードであるという事実(条件付きcatchなど)。

これに対処するために、async/awaitが安定したノードに到達するまで待つ傾向があります。

サードパーティの依存関係を最小限に抑えたい場合は、ネイティブのPromiseの方が優れている可能性があります。

@google-cloudや他の多くのライブラリと同様に、デフォルトでネイティブPromiseを使用し、サードパーティライブラリを使用するpromiseパラメータを受け入れることができます。

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

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

@tgriesserこれは古い問題ですが、stableとCarbonの両方でasync / awaitがあります。

async / awaitがネイティブpromiseで動作することが好ましいとすると(仕様によれば、 async関数はネイティブpromiseを返す必要があります)、これはより高い優先度ですか?

ネイティブプロミスサポートがKnexが実現したいと考えているものであるが、現在は注目されていない場合、PRは歓迎されますか?

御時間ありがとうございます。

@malexdev async関数は、現在knexとはあまり関係のないネイティブの約束を返します。 awaitが正しく機能するためにネイティブの約束が必要であるという意味ではありません。 これが問題/利点である場合(1つの依存関係を削除することを除く)を明確にできますか?

そうは言っても、私はブルーバードを落とすことに反対していませんが、実際にはかなりの内部変更が必要です。 古い機能が一部の構成スイッチの下に隠されており、デフォルトでネイティブPromiseが使用されない限り、現在bluebirdからknexAPIに公開されているいくつかのメソッドを実装する必要がある場合があります。 この方法で移行することは、knexがbluebirdの約束を返すという事実を中継している人々にとって不可能ではありません。

@elhigu個人的な主な利点は、ネイティブプロミスがawaitで使用されることを強制するTSLintルールでTypeScriptを使用していることです。したがって、すべてのKnex呼び出しをPromise.resolve()内にラップする必要があります。 ただし、これはKnexとは特に関係がなく、私に固有の問題である可能性があります。

それ以外は、サードパーティの依存関係が少ない方が良いと思います。 @ c0bが述べたように、オプションを増やすことは決して悪いことではありません。

Knexが興味を持っているのであれば、これには多くの作業が必要になることを理解しています。これに時間を費やすことができてうれしい理由の1つです。

ええ、私はタイプスクリプトの問題からここに着陸しました-私はマルチストレージデータストアライブラリのSQLエンジンとしてKnexを使用しています、そして私はネイティブ対ブルーバードの約束に対して曖昧ですが、私は簡単に使用することはできませんこのため、knexのtypescript。 私はknexthenablesをネイティブ仕様に従っているものとして扱います(私はbluebird拡張機能を使用していません)が、typescriptはBluebirdを返すことについて私を悩ませますメソッド宣言がPromiseの場合

これは、promiseの実装とknexの型付け(異なる開発者によって処理される)の両方を扱っているため、ここでは2つのレベルの深さですが、基本的にここで立ち往生しています-技術的には型の契約を破っていますPromiseを宣言したときにBluebirdを返す(Promise apiにはBluebirdがサポートしていないものがあると思いますか?)が、 return Promise.resolve(bluebirdthing)の束をどこにでも置く気はありません。

私は過去1年間、knexの根性を掘り下げて、ここで何かを取り上げて、人々が望むならPromiseの実装をモジュール化するためのPRに取り組むことをいとわないという一般的な約束に取り組んできました。 PRにオープンですか? 最終的には@elhiguが述べたようなものになります。インスタンス化時に渡されたPromiseコンストラクターを使用するようにユーティリティ関数の一部を再実装して、コードの書き換えの必要性を回避します。 もちろん、パフォーマンスについてはわかりませんが、それはベンチマークできるものです。

async / awaitですべてを完成させるのもクールで、これを修正するのに急いでいません(最終的には、私のユースケースでは、 anyとしてフラグを立てて、それらに対処することになります。 typescriptではなくjavascriptであるかのようにcodebranches)。

@ericeslinger使用されている実際のPromise実装がTypeScriptで問題を引き起こす理由がわかりません。私は、ブルーバードとネイティブのPromiseを1年半問題なく混合してきました...

私はブルーバードのタイプを紹介するタイピングを使用していません。タイプスクリプトのタイピングに、それらは通常のPromiseであり、違いは見られないことを伝えます(たとえば、ブルーバードの特別なメソッドを使用しようとすると文句を言います)。

どのtypescriptバージョンで、再現するサンプルプロジェクトはありますか...たとえば、npmstartスクリプトを使用したgithubリポジトリ。

これは、promiseの実装とknexの型付け(異なる開発者によって処理される)の両方を扱っているため、ここでは2つのレベルの深さですが、基本的にここで立ち往生しています-技術的には型の契約を破っていますPromiseを宣言したときにBluebirdを返す(BluebirdがサポートしていないものがPromise APIにあると思いますか?)が、Promise.resolve(bluebirdthing)をどこにでも返すことは本当に好きではありません。

それらのknexタイピングはnpmからのものですか? ブルーバードはnpmから入力していますか? どのパッケージですか? なぜそうなるのか理解するのに苦労しています。 別のBluebirdタイプがある場合は、ネイティブのPromiseから継承し、Promiseを返すことを通知するAPIから戻っても問題ありません。 説明から、問題はタイピングの実装が非常に壊れているように思われます。 そして、それはこの問題には関係ありません(typescriptは実際の型を気にしないので、knexが何を返すかはわかりません)。

@types/knexをインストールして、 DefinitelyTypedリポジトリからタイピングを取得しています。 その定義は@types/bluebirdをプルし、すべてのknexメソッドはBluebirdオブジェクトを返すものとして入力されます。

具体的には、これはできません。

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

これらのタイピングを使用します。 これは、knex.select()。then()が入力されてDefinitelyTypedリポジトリにBluebirdが返され、それらがチェーンしてさらにBluebirdが作成され、fooがBluebirdの場合はreturn foo as Promise<any>()のように言うためです。BluebirdsはPromisesに割り当てることができないため(少なくともtypescript 2.4では失敗します)(これによると[Symbol.toStringTag]がないため、小さなエラーではありますが、一方を他方に強制することはエラーになります)。

代わりに私はに変更することができます

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

または、他のトリックを実行して、knexへのすべての呼び出しをネイティブのPromise.resolve()呼び出し内にラップします。 これにより、タイプスクリプトはライブラリ関数の下流で文句を言うのをやめ、ライブラリ関数内でknexタイピングを使用できるようになります。

昨日まで、私は@types/knexをまったく使用していませんでした-私はknexをanyとして入力していました。 コードは実行時にどちらの方法でも正常に機能します(少なくとも私のユースケースでは)、それはただです

@elhigu :問題はタイピングの実装が壊れているわけではありません。
TypeScriptは、 async関数のタイプを$# Promise<[type]>として設定します。これは、JS仕様に従って正しいものです。
KnexはBluebird<[type]>を返します。これは、タイピングが正確に反映しています。

私はブルーバードのタイプを紹介するタイピングを使用していません。タイプスクリプトのタイピングに、それらは通常のPromiseであり、違いは見られないことを伝えるだけです。

Knex関数は実際にBluebirdを返すため、これはコンパイラにあります。 興味がない。
BluebirdsはPromisesと互換性があるというのは正しいですが、TypeScriptとの取引の一部は、あなたが返していると言っているものを実際に返すことです。

Promiseを返すように入力された関数からBluebirdを返す場合、タイプBluebirdはタイプPromiseと同じではないため、TypeScriptは文句を言います。
私たちができるさまざまなトリックがあります( @ericeslingeranyの使用、またはPromise.resolve()でラップすることについて述べたことなど)が、結局のところ、そのようなトリックは私たちの多くを失うことになりますTypeScriptが提供するもの。

結局のところ、現実には、「ネイティブのPromiseを使用することは私たちにとって重要であり、Promiseの機能をより一般的にするための作業に積極的に取り組んでいます」と言っているユーザーが少なくとも2人います。

あなたはただ助けようとしているだけだと思いますが、率直に言って、「この方法でそれを行うことができます」と聞く代わりに、私が提案した約束の変更が受け入れられるかどうかを聞きたいです/ @ericeslinger / @ c0b PRか何かで。

@malexdev @ericeslinger詳細情報をありがとう! Promiseから独自のクラスを継承することは実際には不可能のようです。そのため、Promise<>と入力された関数からBluebirdsを返すことが失敗する理由かもしれません:(

@ericeslingerとにかく、 async関数を作成する場合、結果を内部でネイティブpromiseに自動的にラップするため、これは問題になりません。 以下は問題なく準拠し、@ types / bluebirdから入力し、ES2015または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);
});

出力:

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

したがって、今のところknex APIを使用している場合は、bluebirdをネイティブPromisesに自動的にラップする非同期関数/メソッドを使用していない限り、Bluebirdを返すことを実際に通知する必要があります。

@malexdev

Knex関数は実際にはBluebirdsを返すため、これはコンパイラにあります。 興味がない。
BluebirdsはPromisesと互換性があるというのは正しいですが、TypeScriptとの取引の一部は、あなたが返していると言っているものを実際に返すことです。

実際にtypescriptを扱うのは、返されたオブジェクトがインターフェイスを正しく実装するだけで十分です。たとえば、これは完全に問題ありません。

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

それはinstanceofを台無しにしますが、typescriptは実際には、ネイティブのPromiseインスタンスを返すことを約束しておらず、インターフェースのみを返しています。

Promiseを返すように入力された関数からBluebirdを返す場合、BluebirdタイプはPromiseタイプと同じではないため、TypeScriptは文句を言います。
私たちができるさまざまなトリックがあります( @ericeslingerがanyの使用やPromise.resolve()でのラッピングについて述べたことなど)が、結局のところ、そのようなトリックはTypeScriptが提供するものの多くを失うことになります。

悪いタイピングを満たすためだけに、そのようなトリックを実行したり、JSの実装を変更したりする必要がある人を見たくありません。

結局のところ、現実には、「ネイティブのPromiseを使用することは私たちにとって重要であり、Promiseの機能をより一般的にするための作業に積極的に取り組んでいます」と言っているユーザーが少なくとも2人います。

あなたはただ助けようとしているだけだと思いますが、率直に言って、「この方法でそれを行うことができます」と聞く代わりに、私が提案した約束の変更が受け入れられるかどうかを聞きたいです/ @ericeslinger / @ c0b PRか何かで。

理解していただきありがとうございます:)ネイティブプロミスを使用するためのknexの変更は、昨年のある時点ですでに開始および実装されていましたが、 @ tgriesserによって元に戻されたため、今のところ、この変更を開始しない方がよいと思います。

また、このスレッドで言及されているこれらのtypescriptの問題は、knexの実装の問題ではなく、型の宣言の問題(bluebirdがPromiseを正しく実装しない理由...私はそれをさらに深く掘り下げる必要がありますか?)と考えています。

そうは言っても、私はここで2つの別々の問題を見るだけで、ある期間にブルーバードを取り除くことに反対していません。

:+1:ネイティブPromiseの場合。

@elhigu

実際にtypescriptを扱うのは、返されたオブジェクトがインターフェイスを正しく実装するだけで十分であるということです。

けっこうだ。 私はまだ依存関係が少なく、選択肢が多い方が良いという私の意見を支持していますが、タイピングの失敗についてあなたが何を意味していたかがわかりました。

ですから、ネイティブのPromiseについてはまだ👍(私はまだ支援したいと思っています)ですが、Bluebirdの入力を修正することで、当面の問題を解決できるようになりました。 情報をありがとう。

ですから、私はTypeScriptをかなり使い始めました。私は両方ともそれが大好きで、ここでの問題が本当の問題点であることに気づきました。 async / awaitが最近のノードランドでより多くの足場を築くにつれて、Bluebirdユーティリティfns( mapreducetapbindreturn )は役に立たなくなります。 私はBluebirdを内部で使い続けることでかっこいいと思いますが、追加のユーティリティメソッドをすべて返すknexクエリビルダーのパブリックAPIを公式に「非推奨」にします。

これをマージすると、TypeScript定義をアップグレードしてBluebirdの型を削除し( toBluebirdを除く)、Bluebirdの型をPromiseの型に変更できます。

誰かがこれに取り組みたい帯域幅を持っているなら、これが私が行動計画のために考えていることです:

  • []すべてのBluebirdプロキシメソッド( tapmapreducebindreturn )に非推奨の警告を追加します。
  • [] Bluebirdを使い続けたい人のための移行パスとなる.toBluebird()メソッドを追加します(上記のメソッドのすべての呼び出しを簡単に検索/置換して、前に追加するだけで可能です)それらは呼ばれます)
  • []これがマージ/新しいリリースカット(0.15)になったら、Typescript定義を更新できます
  • []最終的には、これらのメソッドを完全に削除できます。 この単純なAPIは、最終的にネイティブPromisesを使用し、意味のあるときにasync/awaitを使用するための道を開きます。

これが理にかなっているかどうか、そして誰かがこれを撮影することに興味があるかどうかを教えてください。

間違いなくこれを手伝うことに興味があります。

これは、Knexが独自のTypeScript定義の維持を開始することを意味しますか? 自動生成されたタイピングでは決してサポートされないジェネリックスを使って、いくつかのクールなことを行うことができます。

このフォークは、ネイティブPromiseのサポートを追加する最初の試みとして開始しました。
https://github.com/tgriesser/knex/pull/2523/files

2016年のこのコメントのように:

好奇心が強い:より速いライブラリをより遅いビルトインに置き換えたい

2年間でどれだけ変化するかは驚くべきことです。

Promiseを返すように入力された関数からBluebirdを返す場合、BluebirdタイプはPromiseタイプと同じではないため、TypeScriptは文句を言います。

@malexdev実際、typescriptは構造型を使用します(フローは名目型を使用し、記述どおりに機能します)。型がPromiseインターフェイスを満たす限り、明示的にextendsであるかどうかにかかわらず、 Promiseと互換性があります。 extends / implementsかどうか。

これはどのように進んでいますか? 良い最初のステップは、knex内のBluebird固有のメソッド呼び出しを除外することだと思います(つまり、実際にはまだ削除しないでください)。 bluebirdを削除し、カスタムPromiseコンストラクターのオプションを提供すると、次のようになります(Bluebirdメソッドを使用しているユーザーにアップグレードパスを提供します)。

異議がなければ、私は最初のステップで作業を開始しません。 既存の作業は停止したようです。

@qubyte変更を行うための積極的な取り組みはないと思います。あちこちで段階的な変更が行われていますが、それだけです。

Ok。 余暇の次のチャンクでは、各メソッドを除外するために、可能な限り小さな変更を加えます。

@tgriesserこれを進める必要がある場合(もしあれば)、何か意見はありますか? 私にとって、来年の4月は、ノード6LTSがラインの終わりに到達するのに妥当な時期のように思えます。

2018年の興味深い情報:

promises-native-async-awaitは、ノード10のpromises-bluebirdよりもパフォーマンスが優れています。
参照: https ://github.com/petkaantonov/bluebird/tree/master/benchmark

したがって、パフォーマンスはもはやブルーバードを維持する理由ではありません。 async/awaitに行く必要があります。

promises-native-async-awaitのパフォーマンスが向上

これは、2016年に私が強く信じていたものでもあります。これは、Nodejsコミュニティのコアであり、サードパーティのライブラリよりも多くの人々がそれを気にかけているという理由だけで、ネイティブの方法がはるかに速く改善されると信じていました。

チケットは選択肢を求めて提出されましたが、競合するPromiseの実装が非常に多いため、bluebirdに永遠に賭けるのは良くありません。

これに関する更新はありますか?

@cfanoulis同じことがまだ立っています。 4月になると、ノード6のサポートを終了し、bluebirdの削除を開始できます。

2019年のアップデートはありますか? / ccをコアコントリビューター、メンテナー、または@hereに関心のある人に: @johanneslumpe @tgriesser @wubzz @elhigu from https://github.com/tgriesser/knex/graphs/contributors?type=c&from=2018-01- 01&to = 2019-12-31

一方、JavaScriptコミュニティは非常にダイナミックで活気に満ち、時には残酷な世界であり、3〜2年ごと(またはそれ以上)に、以前は慣れ親しんだものに置き換わるものがあります。Grunt、Gulp => Webpack、ツール、ライブラリ、フレームワークはあらゆるレベルで完全に競合しているため、古いライブラリでは、イノベーションの導入をやめたり、新しい標準のサポートを遅くしたりする場合(ES2019非同期/イテレータを待つ...)、最終的には置き換えられます

私はいくつかの簡単な調査を行いました。DBORMレベルでは、多くの選択肢もあるようです。TypeORMは良いものかもしれません...(これ以上は言わないでください...)
https://bestofjs.org/tags/db
https://bestofjs.org/projects/typeorm

@c0bccする必要はありません。 とにかくすべてのコメントからメールが届きます。 @kibertoadは、彼の最後のコメントで言わなければならなかったことをすべて言いました...この問題は、非同期/待機ES2019機能をサポートするknexとは何の関係もありません。また、knexはORMではないため、そのコメントが実際に何であったかはわかりません。だいたい。

優れたORMが必要な場合は、objection.jsをお勧めします。 それはknexの上にも実装されています、ここにそれについてのかなり素晴らしいスレッドがありますhttps://github.com/Vincit/objection.js/issues/1069

ある時点で、このknexは置き換えられますが、ORMには置き換えられません。 よりクリーンなコードベースとより一貫性のあるAPIを備えた他のクエリビルダーに置き換えることができます。 たとえば、knex 1.0のように多分;)

また、knexを交換すれば、私はそれで完全に大丈夫です、私にとってはより少ない仕事です:D

私が信じているWIPがあります: https ://github.com/tgriesser/knex-next

async_hooksを使用すると、ネイティブのpromiseを使用しないとhttps://github.com/nodejs/node/issues/22360になり、現在のコンテキストが失われることにも言及したいと思います。

私を信じてください、私たちは移動するために追加の理由を必要としません、私たちはあなたがするのと同じくらい悪いことをしたいです:)。 ただし、ノード6ブランチにさらにいくつかの修正をリリースする必要があります。その後、(最後に)それを削除して、ブルーバードを段階的に段階的に廃止します。

#3227がマージされた後、ようやく開始できます。

前に、この移行でヘルプを使用できるとおっしゃっていましたが、それでもあなたが行きたい方向である場合は、何らかの形でサポートできますか?

私が考えているのは、プロジェクトを作成し、いくつかのタスクを追加して、誰か(おそらく私には時間があります)が割り当てられ、いくつかの日付を設定できるかどうかを確認することです。

@chaffeqaすぐにいくつかのよりきめ細かいタスクを作成し、簡単な変更の最初のラウンドのために#3250を用意します。 主に、bluebird.tap、bluebird.method、bluebird.tryの使用法をネイティブベースのものに置き換える必要があります。 すでに時間があれば、#3250から分岐して、残りの「ブルーバード」に必要なものを確認してみてください( test:sqliteを実行して機能をすばやく検証できるように、方言固有ではないものから始めることをお勧めしますDockerセットアップなし

@qubyte貢献したいなら、今がその時です!

@kibertoad async / awaitを安全に使用できますか?

knexコードベースのことですか? もちろん。 あなた自身のものでは、あなたはいつも:-Dでした

先週MIAになって申し訳ありませんでしたが、私たちの会社の状況は急上昇しているので、そこに集中する必要があります。

アップグレードの大きなブロックの1つであるDisposerの使用法を置き換えることについての議論の一部について、ループを閉じたいと考えていました。

あなたがそれを降り始めるとき、それはかなり深い悩みの種です、それで良いコピー/抽象化を提供するためにいくらかの良いエンジニアリングが必要になります。 何かのパフォーマンスオーバーヘッドがかなり大きくなる可能性があるのではないかと心配しています(promiseチェーンが大きくなるにつれて作成されるオブジェクトがたくさんなります)。

私は実際にいくつかのPOCから始めましたが、これが最も簡単なものだと思います。

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

そして別の:

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

しかし、それらを証明する時間がありませんでした。

この機能はリポジトリに存在する必要があるため(より良いアプローチを考えられない限り...非同期イテレータ?)、他のオプションを実際に検討する価値があると思います(bluebirdを保持しますが、内部でネイティブpromiseを使用するように変換しますか?)。私の腸はそれがbluebirdの実装フックにかなり結びついていると言っていますが、その機能を抽象化することについてさえ、bluebirdチームによる考えを聞きたいです。

この部分を理解できれば、残りのタスクは非常に簡単です。

@chaffeqa Np、まだこれに戻る時間を見つけてくれてありがとう!
ブルーバードの人々が実装を真剣に再設計するための提案を受け入れることは非常に疑わしいです。彼らはこの時点で何よりも安定性に関心があるという点を繰り返し繰り返しており、高度な機能が本当に必要でない限り、実際にネイティブの約束を使用することを推奨していますBluebirdによって提供されます。
Node 8が現在Node.jsの最も人気のあるバージョンであるように思われることを考えると(公式のNode.jsダウンロード統計に基づく)、私たちはまだ非同期イテレーターベースのアプローチに移行できないのではないかと思います。
KnexがDisposablePromiseを内部で実装することにはどのような欠点がありますか? ネイティブのPromiseを拡張しているので、Bluebirdの欠点はなく、ユーザースペースでそれについて知る必要はないと思いますか?

@ericeslinger FWIW、TSタイピングはマスターではもう問題にはならないはずです。ユーザーがBluebirdの機能に依存するのを思いとどまらせるために、ネイティブの約束としてタイピングしています。 これにより、ネイティブPromiseがBluebirdの約束では実装されていないものを実装するときに問題が発生する可能性があるため、使用されているPromiseを可能な限り置き換えたいと考えています。 これらの線に沿った貢献をいただければ幸いです:)

私が考えたナッツ😞
DisposablePromiseのようなことをするのがおそらくこの場合の方法であることに同意します。特に、本当に必要なアイテムがまだ提案されているためです。

欠点は、 DisposablePromiseのようなものを賢明な方法で設計することが非常に重要になることです...そして率直に言って、私の実装が機能するかどうかさえわかりません😆(私は何らかの理由で非同期を考えるのに非常に苦労していますha)。

このスレッドに、この問題を3 u未満の時間で突き刺したい人がいる場合は、

@chaffeqa Bluebirdの実装はどのくらい複雑ですか? たぶん、それを単純に抽出して、ネイティブの約束に追加することができますか?

@chaffeqa最悪のシナリオ-他のすべてのBluebirdの使用法を削除し、触れるのが危険すぎると判断した場合は複雑であるため、これを維持できます。 理想的ではありませんが、最終的にusingが発生します。

残念ながらかなり複雑です...実装は、bluebirdがpromiseのライフサイクルを制御しているという事実に便乗しています。 最善のアプローチは、それが何をしようとしているのかを確認し(上記のusingのリンクにかなり近い)、可能な限りシンプルでパフォーマンスの高いシムを作成することだと思います。

問題は、パイプラインがBluebirdスタイルのpromiseである必要があることです。これは、私が正しく理解していれば、ネイティブpromiseのパフォーマンスに準拠していません(したがって、すべてのトレース+ネイティブ非同期機能が失われます)。

私はむしろ、内部で非同期部分にネイティブの約束を使用するが、コンテキストをバインドし、 disposerのような必要な使用法を実装する機能を提供する何かをしたいと思います。

参考までに、もう1つ頭に浮かぶのは、実際にはknexでのusage.disposerの使用は最小限であるため、このアプローチはそれをより高いレベルに移動するためにうまく機能するのでしょうか。

実験する価値があります:)

ooooもhttps://github.com/petkaantonov/bluebird/issues/1593に基づいて見つけたオプション

いずれにせよ、良い一歩は、前のブランチで開始したことだと思います。ここでは、実際にはBluebirdPromiseであるすべてのPromiseの使用を分離します。これにより、ドロップで遊んでみることができます。 DisposablePromiseBluebirdNativePromiseのような置換で。

@chaffeqa Bluebird.setScheduler(fn => Promise.resolve().then(fn))の部分ですか?
全体的な変換は非常にスムーズに進んでいます! ディスポーザーをBluebirdに保持しながら、内部でネイティブの約束を使用させることができれば、それは実際には良い解決策になる可能性があります。

OK、それでこれらのものはまだ注意が必要です:
https://github.com/tgriesser/knex/issues/3257
https://github.com/tgriesser/knex/issues/3286
https://github.com/tgriesser/knex/issues/3256

助けていただければ幸いです!

async_hooksを使用すると、ネイティブのpromiseを使用しないとnodejs / node#22360になり、現在のコンテキストが失われることにも言及したいと思います。

回避策は、 https://github.com/TimBeyer/cls-bluebirdパッチを使用することです。

参考までに、Nodev8のLTSは今年終了します。

@Bessonovコンテキスト? 最小ノードを10に上げると、この問題にどのように影響しますか? ノード6のサポートはすでに削除されていることに注意してください。

私はknexコードベースに精通していませんが、ブルーバードを取り除くのに役立ついくつかの機能があるかもしれません。 たとえば、ノード10はPromise.finallyをサポートしています。

しかし、とにかく、私はこのトピックの進歩を見てうれしいです:+1:

ディスポーザーパターンについて-使い捨ての約束を返すものにオプションのコールバックを追加するだけでいいですか?
(トランザクションと同じように)

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
}

どのレベルのPromiseライブラリの独立性が必要ですか?
1)すべてネイティブプロミスを使用する
2)内部ネイティブpromise、ユーザーはインターフェースに独自のpromiselibを設定できます
3)ユーザーは内部およびインターフェースにpromiselibを設定できます

この問題の現状はどうなっていますか。 通常、knexはasync awaitで動作しますが、typescriptは、ネイティブのpromiseではないメソッドを待機しているという警告を報告します。

だから、元の問題の質問に答えるために。 現在の回避策は、単に待機して// tslint:disable-next-line: await-promiseのようなものを追加することです。

@maximelkin私はオプション1に投票します。長期的には、すべてのPromiseライブラリが廃止されることを願っています。

次に、この時点では、大多数のブラウザーでさえ、ポリフィルの約束を超えています。

@Bessonovは現在knexに依存しており、ライブラリ(およびおそらくプロジェクト)に依存しています。これには正確にbluebirdが必要です。

私たちは彼らのためにいくつかのフォールバックソリューションを与えるべきです

@Bessonovは現在knexに依存しており、ライブラリ(およびおそらくプロジェクト)に依存しています。これには正確にブルーバードが必要であり、フォールバックソリューションを提供する必要があります。

knexのユーザーがbluebirdに依存しているかどうかは関係ありません。 Knexは引き続きネイティブのプロミスを使用でき、ブルーバードのプロミスとうまく相互運用できます。 フォールバックを絶対に与えてはなりません。

したがって、この問題は、promiseの実装を選択する機能の要求から始まりました。
どこからともなく、それは理由もなくブルーバードを取り除き、すべての扶養家族を壊すことに変化しました。 警告なしで、変更ログ、フォールバックおよびメジャーリリースのオプション。

しかし、私は1.5のtypescriptユーザー全員が今満足していると思います。

したがって、この問題は、promiseの実装を選択する機能の要求から始まりました。
どこからともなく、それは理由もなくブルーバードを取り除き、すべての扶養家族を壊すことに変化しました。 警告なしで、変更ログ、フォールバックおよびメジャーリリースのオプション。

少なくとも以前のknex0.xバージョンは、重大な変更が加えられる可能性のあるメジャーリリースと見なされていたため、0.20.xへの更新のみが安全なアップグレードと見なされるべきでした(バージョン番号<1の場合、semverは本当に緩いです)。

ブルーバードの除去は長い間テーブルにありました、それはこの問題だけではありません。

理由もなくブルーバードを削除する

ブルーバードを削除する理由はありません。 knexpromiseでbluebirdを外部で使用することもできます。 bluebirdを削除する大きな理由の1つは、 async関数が暗黙的にネイティブpromiseを作成することです。したがって、今後Bluebirdを使用し続けるには、理由もなく、追加のbluebirdラッピングコードをknexAPIに追加する必要があります。

警告なしに、変更ログ、

同意しました。 私は最新の変更ログを調べました...残念ながら、バージョン間の重大な変更をリストできなかったようです。 古いAPIを壊す変更を実際に指摘するために、変更ログを作成するときはもっと注意する必要があります。 たとえば、タイピングの変更の多くは、実際には古いTSコードを壊します。

ioredisプロジェクトhttps://github.com/luin/ioredis/commit/da60b8bでも同じ問題が発生しました。 彼らはネイティブプロミスをサポートしたいと思っていました-そしてみんなは本当に良い解決策を作りました-彼らはカスタムプロミスライブラリをサポートするオプションを追加し、デフォルトでネイティブプロミスを使用します。 なぜだめですか? カスタムpromiseライブラリの設定は高速で、すべてのアプリケーションコードにパッチを適用する必要はありません。

Bluebirdを使い続けるには、理由もなくknexAPIに追加のbluebirdラッピングコードを追加する必要があります。

うん。 しかし、明示的に指定されている場合は、モジュール呼び出しをbluebird(またはその他のpromiseライブラリ)でラップしないのはなぜですか? これは1つの単純なラッパーであり、オーバーヘッドはゼロであり、ユーザーは任意のPromiseライブラリを使用できます。 ブルーバードを必要とする人がいない場合、このオプションを使用する人はいないため、時間内に安全に非推奨にすることができます。

また、私はその意見を見ました

長期的には、すべてのPromiseライブラリが廃止されることを願っています。

しかし、私見には2つの間違った仮定があります。

  • より速いのでBluebirdが使用されます。
  • ブルーバードはポリフィルとして使用されます。

非同期を超えた本当に複雑なアプリケーションの場合はそうではないと思います-1つのライナーを待ちます。
Bluebirdには、タイムアウト、カスタムエラー処理、同時実行によるマッピング、キャンセル、削減など、複雑な非同期フローに絶対に必要な多くの機能があります。 これらの機能はすべてネイティブのpromiseで実装できますが、それは多くの役に立たない定型文です。 2020年でも、このボイラープレートをすべて必要としないため、ノード12ではまだbluebirdを使用しています。

なぜだめですか? カスタムpromiseライブラリの設定は高速で、すべてのアプリケーションコードにパッチを適用する必要はありません。

内部でasync-awaitを使用するものはすべて、promiseをネイティブpromiseに強制変換するため、オプションは、各メソッドの出力をカスタムpromiseでラップするか、内部コードでasync-awaitを禁止するようになります。 それは最初の検査で見えるかもしれないほど小さな仕事ではありません。

@qubyte

それは最初の検査で見えるかもしれないほど小さな仕事ではありません。

いいえ、それは私がすでに言ったのと同じくらい簡単です。 エクスポートされた外部関数のラッパーを作成し、それだけです。 約10行のコード。 そして、すべての内部コードを好きなように書いてください。

@jehy :それらを実装する簡単な方法を見つけた場合は、これらの10行のコードのPRを自由に送信してください。

また、今日は回避策を考え出すために時間を費やします。

価値のあることとして、bluebirdのAPIの多くは、これらのパッケージによるネイティブの約束を使用して、同じまたは近いAPIで複製されます: https ://github.com/sindresorhus/promise-fun

価値があるのは、bluebirdのAPIの多くが、これらのパッケージによるネイティブプロミスを使用して同じまたは近いAPIで複製されていることです。

1ではなく最大50のパッケージ? 真剣に?

はい。ただし、ほとんどの場合、必要なのはごくわずかです(たとえば、p-map)。 もちろん、マイレージは異なる場合があります。 それはあなたが望むものへの1つの潜在的なルートとしてのみ提供されます。

@jehy :アプリケーションコード内で一時的な回避策として試すことができるものは次のとおりです。

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

これは、全体的な問題に対する適切な解決策ではありません。 knex内に作成された他の一時オブジェクトがあり、同様の方法で装飾する必要があります。

また、免責事項:回避策☝️のテストはほとんど行われていません。 したがって、アプリケーションのテストを再実行して、何も壊れていないことを確認する必要があります。

ここに2セントを追加したかっただけです。ネガティブフィードバックに関係なく、この移行に費やされたすべての作業に本当に感謝しています。

私たちのアプリの観点からは、knexはBluebirdを要求する最後のライブラリであり、完全なネイティブpromiseサポートに準拠することは次のことを意味します。

  1. スタックトレースを汚すことはもうありません
  2. SSRの重量を適切な量だけ減らしました
  3. ネイティブ非同期待機がbluebirdよりもパフォーマンスが向上したため(そしてますます成長しているため)、パフォーマンスが向上しました。

es標準に向かって走り続けるのはとても大きな勝利です...そして私はそれが図書館のメンテナにとって簡単ではないことを知っているので、皆さんに叫び、そのような負担を負ってくれてありがとう!

変更に苦しんでいる人のために:私たちは恩恵を受けているので助けたいので、デバッグや移行の助けが必要な場合は連絡してください!

@chaffeqaこのフィードバックをありがとう、それは多くのことを意味します!

@jehy :提案された回避策を試す機会がありましたか? もしそうなら、それはあなたの当面の問題を解決しましたか?

このページは役に立ちましたか?
0 / 5 - 0 評価