Rx レスポンシブプログラミングについての簡単な説明

Rx レスポンシブプログラミングについての簡単な説明

1. 観察可能

Observable は文字通り「観察可能」という意味で、言い換えれば「データ ソース」または「イベント ソース」の一種です。このデータ ソースには観察できる機能があり、これはデータを積極的に収集することとは根本的に異なります。わかりやすい比喩を使うと、Observable は蛇口のようなものです。蛇口をオンにして Observable をサブスクライブすると、水、つまりデータが継続的に流れ出ます。これが、能動的から受動的に変化すること、つまりレスポンシブプログラミングの中心的な考え方です。ただし、この記事ではこれについては詳しく説明しません。

Observable は、さまざまな方法で実装できる概念です。この記事では、高階関数を使用して、よく使用される 2 つの Observable、fromEvent と Interval を実装します。 Observable へのサブスクライブとサブスクライブ解除という 2 つの動作を説明することで、読者が Observable が何であるかを真に理解できるようになります。

2. 高階関数

高階関数の概念は関数型プログラミングから来ています。簡単に定義すると、関数の入力パラメータまたは戻り値は関数の関数であるということです。例えば:

関数foo(arg){
    関数()を返す{
        console.log(引数)
    }
}
const bar = foo("hello world")
bar() // こんにちは世界

ps: 高階関数で実行できることは多数ありますが、ここではこの記事で必要な状況にのみ使用されます。

上記の foo 関数の呼び出しは、hello world を直接印刷するのではなく、hello world をキャッシュするだけです。その後、実際のニーズに応じて返された bar 関数を呼び出し、実際に hello world を出力する作業を実行します。

なぜこのパッケージングの手順を実行する必要があるのでしょうか?実際、これを行うと、通話が「遅延」されます。そして、すべての本質は「遅延」という言葉にあります。本質的には、配達ボックスのように、動作を一貫したもののようにパッケージ化しています。

中にはいろいろなものを入れることができますが、物流上は統一されたものになります。そのため、宅配ボックスの積み重ね、輸送、保管、さらにはボックスの開封動作まで一貫した統一された操作が可能になります。

前の例に戻ると、foo 関数を呼び出すことは、宅配ボックスを梱包することと同じです。宅配ボックスには固定プログラムがあり、宅配ボックスが開かれると (bar を呼び出す) 印刷操作が実行されます。

foo1、foo2、foo3 などがあります。さまざまなプログラムがありますが、これらの foo には共通の操作である「open」があります。 (前提として、foo は関数を返すため、「open」操作が満たされ、返された関数が呼び出されます)。

関数foo1(arg){
    関数()を返す{
       console.log(引数+"?")
    }
}
関数foo2(arg){
      関数()を返す{
         console.log(引数+"!")
     }
}
const bar1 = foo1("こんにちは世界")
const bar2 = foo2("はい")
bar1()+bar2() // こんにちは、世界? はい!

3. エクスプレスボックスモデル

3.1. エクスプレスボックスモデル1: fromEvent

上記を基礎として、Rx プログラミングで最もよく使用される Observable である fromEvent(……) を見てみましょう。 Rx プログラミングの初心者にとって、最初は fromEvent(...) と addEventListener(...) の違いを理解するのは難しいです。

btn.addEventListener("クリック",コールバック)
rx.fromEvent(btn,"click").subscribe(コールバック)

このコードを直接実行すると、効果は確かに同じになります。それで、違いは何でしょうか?最も直接的な違いは、subscribe 関数は btn ではなく fromEvent(...) に作用するのに対し、addEventListener は btn に直接作用することです。 subscribe 関数は、ある種の「オープン」操作であり、fromEvent(...) は、ある種のエクスプレス ボックスです。

fromEventは実際にはaddEventListenerへの「遅延」呼び出しです

関数 fromEvent(ターゲット,イベント名){
    関数(コールバック)を返す{
        target.addEventListener(イベント名、コールバック)
    }
}
const ob = fromEvent(btn,"クリック")
ob(console.log) // subscribe と同等

おお! fromEventは本質的には高階関数である

「オープン」操作を完了するためにサブスクライブを実装する方法については、この記事の範囲外です。Rx プログラミングでは、このサブスクライブ アクションは「サブスクリプション」と呼ばれます。 「サブスクリプション」は、すべての Observable の統一された操作です。もう一度言いますが、この記事の Observable への「呼び出し」は論理的には subscribe と同等です。

もう 1 つの例を見てみましょう。基本的に、読者は 2 つの例から N 通りの推論を導き出すことができます。

3.2. エクスプレスボックスモデル2: 間隔

Rx には間隔があります。それと setInterval の違いは何ですか?

すでに誰かが答え始めていると思いますが、interval は setInterval への遅延呼び出しです。ビンゴ!

関数間隔(期間){
    i = 0とする
    関数(コールバック)を返す{
        setInterval(period,()=>callback(i++))
    }
}
定数ob = 間隔(1000)
ob(console.log) // subscribe と同等

上記の 2 つの例から、fromEvent(...) にしろ Interval(...) にしろ、内部ロジックはまったく異なりますが、どちらも「エクスプレス ボックス」と同じものに属しており、これを Observable と呼びます。

fromEvent と Interval は、「エクスプレス ボックス」を作成するためのモデルにすぎません。呼び出し後に返されるのは「エクスプレス ボックス」のみです。つまり、fromEvent(btn,"click")、interval(1000) などです。

4. 高級エクスプレスボックス

上記の基礎を踏まえて、前進を始めましょう。これで、エクスプレスボックスが大量に揃ったので、再梱包することができます。

冒頭でも述べたように、エクスプレスボックスはいくつかの操作を統合しているため、多くのエクスプレスボックスを積み重ねて大きなエクスプレスボックスを形成することができます。この大きなエクスプレス ボックスには、小さなエクスプレス ボックスと同じ「開く」操作 (つまり、サブスクライブ) があります。この大きな宅配ボックスを開けると何が起こるのでしょうか?

小さな箱を一つずつ開ける(連結)、すべての小さな箱を一度に開ける(マージ)、開けやすい箱だけを開ける(競争)など、さまざまな可能性があります。

マージ メソッドの簡略化されたバージョンを次に示します。

関数 merge(...obs){
    関数(コールバック)を返す{
        obs.forEach(ob=>ob(callback)) // すべてのエクスプレスボックスを開く}
}

先ほどの fromEvent と interval を例に挙げてみましょう。

2 つの Observable を結合するには、 merge メソッドを使用します。

const ob1 = fromEvent(btn,'click') // エクスプレスボックス1を作成する
const ob2 = interval(1000) // エクスプレスボックス2を作成する
const ob = merge(ob1,ob2) //大きなエクスプレスボックスを作成する ob(console.log) //大きなエクスプレスボックスを開く

大きなエクスプレス ボックス ob を「開く」(サブスクライブする) と、2 つの小さなエクスプレス ボックスも「開く」(サブスクライブする) ようになり、小さなエクスプレス ボックス内のロジックが実行されます。2 つの Observable を 1 つにマージします。

これが、さまざまな非同期関数を Observable にカプセル化して、それらの操作を統一的に行えるようにするために私たちが一生懸命取り組んでいる理由です。もちろん、単に「開く」(購読する)というのは最も基本的な機能であり、これから高度な機能へと進んでいきます。

5.宅配ボックスを破壊する

5.1. 宅配ボックスを破棄する - 定期購読をキャンセルする

fromEvent を例に挙げてみましょう。以前、addEventListener のラッパーとして単純な高階関数を作成しました。

関数 fromEvent(ターゲット,イベント名){
    関数(コールバック)を返す{
        target.addEventListener(イベント名、コールバック)
    }
}

この関数を呼び出すと、配達ボックスが生成されます (fromEvent(btn,'click'))。この関数によって返された関数を呼び出すと、エクスプレス ボックスが開きます (fromEvent(btn,'click')(console.log))。

では、この開封済みの宅配ボックスをどうやって破棄するのでしょうか?

まず、開いた宅配ボックスを取得する必要があります。上記の関数呼び出しの結果は void であり、これを使用して何もできないため、開いた宅配ボックスを構築する必要があります。高階関数の考え方を引き続き使用します。つまり、破棄操作のために返された関数内で別の関数を返します。

関数 fromEvent(ターゲット,イベント名){
    関数(コールバック)を返す{
        target.addEventListener(イベント名、コールバック)
        関数()を返す{
            target.removeEventListener(イベント名、コールバック)
        }
    }
}
const ob = fromEvent(btn,'click') // 宅配ボックスを作成する const sub = ob(console.log) // 宅配ボックスを開き、それを破棄するために使用できる関数を取得する sub() // 宅配ボックスを破棄する

同様に、間隔についても同じことができます。

関数間隔(期間){
    i = 0とする
    関数(コールバック)を返す{
        id = setInterval(period,()=>callback(i++)) とします。
        関数()を返す{
            クリア間隔(id)
        }
    }
}
const ob = interval(1000) // 宅配ボックスを作成する const sub = ob(console.log) // 宅配ボックスを開く sub() // 宅配ボックスを破棄する

5.2. 高級宅配ボックスを破壊する

マージを例に挙げてみましょう。

関数 merge(...obs){
    関数(コールバック)を返す{
        const subs = obs.map(ob=>ob(callback)) // すべてを購読し、すべての破壊関数を収集します return function(){
            subs.forEach(sub=>sub()) // 破棄関数を走査して実行する}
    }
}
 
const ob1 = fromEvent(btn,'click') // エクスプレスボックス1を作成する
const ob2 = interval(1000) // エクスプレスボックス2を作成する
const ob = merge(ob1,ob2) // 大きなエクスプレスボックスを作成する const sub = ob(console.log) // 大きなエクスプレスボックスを開く sub() // 大きなエクスプレスボックスを破棄する

大型宅配ボックスを破棄する際、中の小型宅配ボックスもすべて一緒に破棄させていただきます。

6. 補足

ここまでで、Observable の 2 つの重要な操作 (サブスクリプションとサブスクリプション解除) について説明しました。サブスクリプション解除の動作は Observable ではなく、「開かれた」エクスプレス ボックス (Observable にサブスクライブした後に返されるもの) に対して実行されることに注意してください。

これに加えて、Observable には、イベントの発行と完了/例外という 2 つの重要な操作があります (これら 2 つの操作は Observable によって開始されるコールバックであり、操作の反対方向であるため、操作と呼ぶことはできません)。

Express Box では、これら 2 つの動作はそれほど鮮明ではありません。Observable は蛇口に例えることができます。Express Box の本来のオープンは蛇口をオンにすることになり、渡されたコールバック関数は水を集めるカップに例えることができます。コールバック関数についてはすでによく知られているため、この記事では詳細には触れません。

VII. 追記

学んだことをまとめると、高階関数を通じていくつかの操作を「遅延」し、統一された動作を与えました。たとえば、「subscribe」は非同期関数の実行を遅延し、「unsubscribe」は上記に基づいてリソース破棄関数の実行を遅延します。

これらのいわゆる「遅延」実行は、理解するのが最も難しく、舞台裏で Rx プログラミングの最も重要な部分です。 Rx の本質は、非同期関数をカプセル化し、それをサブスクリプション、サブスクリプション解除、イベント発行、完了/例外という 4 つの主要な動作に抽象化することです。

Rxライブラリを実際に実装する方法はたくさんあります。この記事では、Observableの本質を誰もが理解できるように、高階関数の考え方のみを使用します。正式版では、Observableボックスは高階関数ではなくオブジェクトですが、本質的には同じです。

上記は、Rx レスポンシブ プログラミングの詳細についての簡単な説明です。Rx レスポンシブ プログラミングの詳細については、123WORDPRESS.COM の他の関連記事をご覧ください。

以下もご興味があるかもしれません:
  • TypeScriptでのRxJSの簡単な使い方の詳しい説明
  • RxJava でサブスクリプションをキャンセルするさまざまな方法を実装する
  • RxJava2 の例外と処理方法について話す
  • RxJS JavaScriptフレームワークCycle.jsを学ぶ
  • RxJS 入門と予備的なアプリケーション
  • このキーがないために RxJava に慣れていない (推奨)
  • RxJava2 スレッドスケジューリング方法
  • RxJava メッセージ送信とスレッド切り替えの実装原理
  • RxJava2 と Retrofit2 のカプセル化チュートリアル (わかりやすく、シンプルで実用的)

<<:  InnoDB テーブルの BLOB 列と TEXT 列のストレージ効率を最適化します。

>>:  Centos8で静的IPを設定する方法の詳細な説明

推薦する

Navicateを使用してAlibaba Cloud Server上のMySQLに接続する

1. まず、サーバーの mysql にアクセスして権限を変更します。 GRANT オプション付きで、...

Vue でバイナリ ファイル ストリームを受信して​​ PDF プレビューを実現する方法

バックグラウンド コントローラー @RequestMapping("/getPDFStre...

MySQLで論理SQLを置き換える際の落とし穴を回避する方法の詳細な説明

重複キーの置換と挿入の違い置換の使用法競合がない場合、挿入と同等となり、他の列のデフォルト値が使用さ...

高性能ウェブサイトの最適化ガイド

パフォーマンスの黄金律:エンドユーザーの応答時間のわずか 10% ~ 20% が HTML ドキュメ...

集める価値のある 15 個の JavaScript 関数

目次1. 数字を逆にする2. 配列内の最大のn個の数値を取得する3. 階乗を計算する4. 現在の動作...

js でオブジェクトを作成するさまざまな方法とその長所と短所のまとめ

目次初期作成方法ファクトリーパターンコンストラクターパターンコンストラクタパターンの最適化プロトタイ...

Ubuntuが仮想マシンでインターネットに接続できない問題の解決策

インターネットに接続できない仮想マシンをセットアップするのは非常に面倒です。ここでは、Ubuntu ...

ウェブデザインを改善するための 8 つの CSS ツールを共有する

ウェブサイトのデザインを編集または変更する必要がある場合、CSS が重要な役割を果たします。 CSS...

CSSはリモコンのボタンを模倣する

注: このデモはミニプログラム環境でテストされており、他の h5 および pc Web ページにも適...

Linux でディスクをマウントし、起動時に自動的にマウントするように設定する方法

皆さんの時間は貴重だと承知しているので、プロセス コマンドを直接書き留めておきます。設定できます。原...

MySQL はエンタープライズレベルのログ管理、バックアップ、リカバリの実践的なチュートリアルを実装します

背景事業が発展するにつれ、会社の事業内容や規模は拡大し続け、ウェブサイトには大量のユーザー情報やデー...

MySQL スケジュール バックアップ ソリューション (Linux crontab を使用)

序文この世の愛には値段のつくものもありますが、データには値段のつけられないものがあります。将来、誤っ...

tomcatでcatalina.outログをカットする3つの方法の詳細な説明

1. ログセグメンテーションのためのLog4j 1) log4j-1.2.17.jar、tomcat...

dockerコンテナにviコマンドをインストールする簡単な操作

docker コンテナを使用する場合、vim がインストールされていないことがあり、vim コマンド...

Viteプロジェクトを作成する手順

目次序文yarn create は何をしますか?ソースコード分析プロジェクトの依存関係テンプレート構...