新年の始まりです。多くの人がもっとアクティブになることを約束していますが、私はPromise
をもっと怠惰にする方法を紹介します…Promise
、つまり。
すぐに理解できるようになります。
まず、基本的なPromise
の例を見てみましょう。ここに、ミリ秒単位の時間と値を取る sleep という関数があります。待機する必要があるミリ秒数の間setTimeout
を実行する promise を返します。 Promise はその値で解決されます。
/** * @template ValueType * @param {number} ms * @param {ValueType} value * @returns {Promise<ValueType>} */ function sleep(ms, value) { return new Promise((resolve) => { setTimeout(() => resolve(value), ms); }); }
それはこのように動作します:
引数1000
と'Yawn & stretch'
を指定してsleep
関数を待機すると、1 秒後にconsole
に文字列 'Yawn & Stretch' が記録されます。
それについて特別なことは何もありません。おそらく期待どおりに動作しますが、返されたPromise
をすぐにawait
するのではなく、変数として保存して後でawait
すると、少し奇妙になります。
const nap = sleep(1000, 'Yawn & stretch')
ここで、時間がかかる他の作業 (次の例を入力するなど) を行い、 await
変数をnap
するとします。
解決するまでに 1 秒の遅延が予想されるかもしれませんが、実際にはすぐに解決されます。 Promise
を作成するときはいつでも、それが担当する非同期機能をインスタンス化します。
この例では、 nap
変数を定義した瞬間に、 setTimeout
を実行するPromise
が作成されます。私はタイピングが遅いので、待機するまでにPromise
はawait
されます。
つまり、 Promise
は熱心です。彼らはあなたが彼らをawait
のを待ちません。
場合によっては、これは良いことです。それ以外の場合は、不要なリソースの使用につながる可能性があります。これらのシナリオでは、 Promise
のように見えるものが必要になる場合がありますが、
先に進む前に、興味深いことをお見せしたいと思います。
JavaScript でawait
できるのはPromise
だけではありません。 .then()
メソッドでプレーンなObject
を作成すると、 await
と同じように実際にそのオブジェクトをPromise
できます。
これはちょっと奇妙ですが、 Promise
のように見えてもそうではないさまざまなオブジェクトを作成することもできます。これらのオブジェクトは「
それを念頭に置いて、新しいものを作成しましょうPromise
コンストラクターを拡張するLazyPromise
と呼ばれます。 Promise の拡張は厳密には必要ではありませんが、 instanceof
などを使用してPromise
に似せたものにします。
class LazyPromise extends Promise { /** @param {ConstructorParameters<PromiseConstructor>[0]} executor */ constructor(executor) { super(executor); if (typeof executor !== 'function') { throw new TypeError(`LazyPromise executor is not a function`); } this._executor = executor; } then() { this.promise = this.promise || new Promise(this._executor); return this.promise.then.apply(this.promise, arguments); } }
注目すべき部分はthen()
メソッドです。標準のPromise
のデフォルトの動作をハイジャックして、実際のPromise
を作成する前に.then()
メソッドが実行されるまで待機します。
これにより、実際に呼び出すまで非同期機能のインスタンス化が回避されます。そして、明示的に.then()
を呼び出しても、 await
を使用しても機能します。
それでは、元のsleep
関数のPromise
をLazyPromise
に置き換えるとどうなるか見てみましょう。もう一度、結果をnap
変数に代入します。
function sleep(ms, value) { return new LazyPromise((resolve) => { setTimeout(() => resolve(value), ms); }); } const nap = sleep(1000, 'Yawn & stretch')
次に、時間をかけてawait nap
行を入力して実行します。
今回は、変数が作成されてからの経過時間に関係なく、 Promise
が解決されるまでに 1 秒の遅延が見られます。
(この実装は新しいPromise
を 1 回だけ作成し、その後の呼び出しでそれを参照することに注意してください。したがって、再度await
すると、通常のPromise
と同様にすぐに解決されます)
もちろん、これは製品コードではおそらく見られない些細な例ですが、遅延評価されたPromise
のようなオブジェクトを使用するプロジェクトはたくさんあります。おそらく最も一般的な例は、データベースORMと次のようなクエリビルダーです。
以下の疑似コードを検討してください。これは、次のクエリ ビルダーのいくつかに触発されています。
const query = db('user') .select('name') .limit(10) const users = await query
"user"
テーブルに移動し、最初の 10 個のエントリを選択してそれらの名前を返すデータベース クエリを作成します。理論的には、これは通常のPromise
でうまく機能します。
しかし、クエリ文字列パラメーターなどの特定の条件に基づいてクエリを変更したい場合はどうすればよいでしょうか?最終的にPromise
を待つ前に、クエリの変更を続行できると便利です。
const query = db('user') .select('name') .limit(10) if (orderBy) { query.orderBy(orderBy) } if (limit) { query.limit(limit) } if (id) { query.where({ id: id }) } const users = await query
元のデータベース クエリが標準のPromise
である場合、変数を割り当てるとすぐにクエリが積極的にインスタンス化され、後で変更することはできません。
遅延評価を使用すると、このようなコードを記述して、より簡単に追跡でき、開発者のエクスペリエンスが向上し、必要なときに 1 回だけクエリを実行できます。
これは、遅延評価が優れている 1 つの例です。また、HTTP 要求の構築、変更、調整などにも役立つ場合があります。
Lazy Promise
は適切なユースケースには非常に優れていますが、すべてのPromise
を置き換える必要があるとは言えません。場合によっては、積極的にインスタンス化して、できるだけ早く応答を準備することが有益です。
これは、「場合による」シナリオの 1 つです。しかし、次に誰かがあなたにPromise
をするように頼んだら、それについて怠けていると考えてください ( ͡° ͜ʖ ͡°)。
読んでいただきありがとうございます。この記事が気に入ったらどうぞ
最初に公開された