Skip to content
Mithril.js 2
Main Navigation ガイドAPI

日本語

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
한국어
Italiano
Polski
Türkçe
čeština
magyar

日本語

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
한국어
Italiano
Polski
Türkçe
čeština
magyar

外観

Sidebar Navigation

API

コア API

m(selector, attributes, children)

render(element, vnodes)

mount(root, component)

route(root, defaultRoute, routes)

request(options)

parseQueryString(string)

buildQueryString(object)

buildPathname(object)

parsePathname(文字列型)

trust(html)

fragment(attrs, children)

redraw()

censor(object, extra)

オプション API

stream()

ガイド

このページの内容

stream() ​

説明 ​

Stream(ストリーム)は、スプレッドシートアプリケーションのセルと同様に、リアクティブなデータ構造です。

例えば、スプレッドシートにおいて A1 = B1 + C1 である場合、B1 または C1 の値を変更すると、A1 の値も自動的に変更されます。

同様に、あるストリームを別のストリームに依存させることで、一方の値を変更すると、もう一方が自動的に更新されるようにすることができます。これは、計算コストが高く、例えば再描画のたびに実行するのではなく、必要な場合にのみ実行したい場合に便利です。

ストリームは、Mithril.js のコアディストリビューションには含まれていません。ストリームモジュールを利用するには、以下のように記述します。

javascript
var Stream = require('mithril/stream');

バンドルツールチェーンが利用できない環境では、モジュールを直接ダウンロードすることもできます。

html
<script src="https://unpkg.com/mithril/stream/stream.js"></script>

<script> タグで直接ロードした場合(require ではなく)、ストリームライブラリは window.m.stream として利用可能になります。window.m が既に定義されている場合(例えば、メインの Mithril.js スクリプトも使用している場合)、既存のオブジェクトにアタッチされます。それ以外の場合は、新しい window.m が作成されます。ストリームを Mithril.js と組み合わせて生のスクリプトタグとして使用する場合は、mithril/stream によって定義された window.m オブジェクトが mithril によって上書きされないように、mithril の前に mithril/stream をページに含める必要があります。これは、ライブラリが CommonJS モジュールとして消費される場合(require(...) を使用)には問題になりません。

シグネチャ ​

ストリームを生成します。

stream = Stream(value)

引数型必須説明
valueanyいいえこの引数が存在する場合、ストリームの初期値として設定されます。
戻り値Streamストリームを返します。

シグネチャの読み方

静的メンバー ​

Stream.combine ​

依存するストリームのいずれかが更新されるとリアクティブに更新される算出ストリームを生成します。ストリームの結合を参照してください。

stream = Stream.combine(combiner, streams)

引数型必須説明
combiner(Stream..., Array) -> anyはいcombiner引数を参照。
streamsArray<Stream>はい結合するストリームのリスト
戻り値Streamストリームを返します。

シグネチャの読み方

combiner ​

計算されたストリームの値を生成する方法を指定します。ストリームの結合を参照してください。

any = combiner(streams..., changed)

引数型必須説明
streams...Streams のスプレッド構文いいえstream.combine の 2 番目の引数として渡されたストリームに対応する、ゼロ個以上のストリームのスプレッド構文
changedArray<Stream>はい更新によって影響を受けたストリームのリスト
戻り値any計算された値を返します。

シグネチャの読み方

Stream.merge ​

複数のストリームの値を配列として持つストリームを生成します。

stream = Stream.merge(streams)

引数型必須説明
streamsArray<Stream>はいストリームのリスト
戻り値Stream入力ストリームの値の配列を値とするストリームを返します。

シグネチャの読み方

Stream.scan ​

アキュムレータと入力値を使って関数を適用した結果を持つ新しいストリームを生成します。

アキュムレーター関数内で特別な値 stream.SKIP を返すことで、依存ストリームが更新されないようにすることができます。

stream = Stream.scan(fn, accumulator, stream)

引数型必須説明
fn(accumulator, value) -> result |SKIPはいアキュムレーターと値の引数を取り、同じ型のアキュムレーターの新しい値を返す関数
accumulatoranyはいアキュムレーターの初期値
streamStreamはい値を含むストリーム
戻り値Stream結果を含む新しいストリームを返します。

シグネチャの読み方

Stream.scanMerge ​

ストリームとスキャン関数のペア配列を入力として受け取り、指定された関数を使用してそれらのすべてのストリームを単一のストリームにマージします。

stream = Stream.scanMerge(pairs, accumulator)

引数型必須説明
pairsArray<[Stream, (accumulator, value) -> value]>はいストリームとスキャン関数のタプルの配列
accumulatoranyはいアキュムレーターの初期値
戻り値Stream結果を含む新しいストリームを返します。

シグネチャの読み方

Stream.lift ​

依存するストリームのいずれかが更新されるとリアクティブに更新される計算されたストリームを生成します。ストリームの結合を参照してください。combine とは異なり、入力ストリームは可変数の引数(配列の代わりに)であり、コールバックはストリームではなくストリームの値を受け取ります。changed パラメーターはありません。これは一般的に combine より使いやすい関数です。

stream = Stream.lift(lifter, stream1, stream2, ...)

引数型必須説明
lifter(any...) -> anyはいlifter 引数を参照してください。
streams...Streams のリストはいリフトするストリーム
戻り値Streamストリームを返します。

シグネチャの読み方

lifter ​

計算されたストリームの値を生成する方法を指定します。ストリームの結合を参照してください。

any = lifter(streams...)

引数型必須説明
streams...Streams のスプレッド構文いいえstream.lift に渡されたストリームの値に対応する、ゼロ個以上の値のスプレッド構文
戻り値any計算された値を返します。

シグネチャの読み方

Stream.SKIP ​

下流の処理をスキップさせるためにストリームコールバックに返すことができる特別な値

Stream["fantasy-land/of"] ​

このメソッドは、機能的には stream と同じです。これは、Fantasy Land の Applicative 仕様に準拠するために存在します。詳細については、Fantasy Land とはセクションを参照してください。

stream = Stream["fantasy-land/of"](value)

引数型必須説明
valueanyいいえこの引数が存在する場合、ストリームの初期値として設定されます。
戻り値Streamストリームを返します。

インスタンスメンバー ​

stream.map ​

コールバック関数の結果を値とする依存ストリームを生成します。このメソッドは、stream["fantasy-land/map"] のエイリアスです。

dependentStream = stream().map(callback)

引数型必須説明
callbackany -> anyはい戻り値がストリームの値になるコールバック
戻り値Streamストリームを返します。

シグネチャの読み方

stream.end ​

true に設定すると依存ストリームの登録を解除する共依存ストリーム。終了状態を参照してください。

endStream = stream().end

stream["fantasy-land/of"] ​

このメソッドは、機能的には stream と同じです。これは、Fantasy Land の Applicative 仕様に準拠するために存在します。詳細については、Fantasy Land とはセクションを参照してください。

stream = stream()["fantasy-land/of"](value)

引数型必須説明
valueanyいいえこの引数が存在する場合、ストリームの初期値として設定されます。
戻り値Streamストリームを返します。

stream["fantasy-land/map"] ​

コールバック関数の結果を値とする依存ストリームを生成します。ストリームのチェーンを参照してください。

このメソッドは、Fantasy Land の Applicative 仕様に準拠するために存在します。詳細については、Fantasy Land とはセクションを参照してください。

dependentStream = stream()["fantasy-land/map"](callback)

引数型必須説明
callbackany -> anyはい戻り値がストリームの値になるコールバック
戻り値Streamストリームを返します。

シグネチャの読み方

stream["fantasy-land/ap"] ​

このメソッドの名前は apply の略です。ストリーム a の値が関数の場合、別のストリーム b はそれを b.ap(a) の引数として使用できます。ap を呼び出すと、ストリーム b の値を引数として関数が呼び出され、関数呼び出しの結果が値である別のストリームが返されます。このメソッドは、Fantasy Land の Applicative 仕様に準拠するために存在します。詳細については、Fantasy Land とはセクションを参照してください。

stream = stream()["fantasy-land/ap"](apply)

引数型必須説明
applyStreamはい値が関数であるストリーム
戻り値Streamストリームを返します。

基本的な使い方 ​

ストリームは、コアの Mithril.js ディストリビューションには含まれていません。プロジェクトに含めるには、そのモジュールを require します。

javascript
var stream = require('mithril/stream');

変数としてのストリーム ​

stream() はストリームを返します。最も基本的なレベルでは、ストリームは変数または getter-setter プロパティと同様に機能します。状態を保持し、変更することができます。

javascript
var username = stream('John');
console.log(username()); // logs "John"

username('John Doe');
console.log(username()); // logs "John Doe"

主な違いは、ストリームが関数であるため、高階関数と組み合わせることができる点です。

javascript
var users = stream();

// fetch API を使用してサーバーからユーザーをリクエストする
fetch('/api/users')
  .then(function (response) {
    return response.json();
  })
  .then(users);

上記の例では、リクエストが解決されると、users ストリームに応答データが設定されます。

双方向バインディング ​

ストリームは、イベントコールバックなどからも入力できます。

javascript
// ストリーム
var user = stream('');

// ストリームへの双方向バインディング
m('input', {
  oninput: function (e) {
    user(e.target.value);
  },
  value: user(),
});

上記の例では、ユーザーが入力すると、user ストリームが入力フィールドの値で更新されます。

計算されたプロパティ ​

ストリームは、計算プロパティを実装するのに役立ちます。

javascript
var title = stream('');
var slug = title.map(function (value) {
  return value.toLowerCase().replace(/\W/g, '-');
});

title('Hello world');
console.log(slug()); // logs "hello-world"

上記の例では、slug の値は、slug が読み取られるときではなく、title が更新されるときに計算されます。

もちろん、複数のストリームに基づいてプロパティを計算することも可能です。

javascript
var firstName = stream('John');
var lastName = stream('Doe');
var fullName = stream.merge([firstName, lastName]).map(function (values) {
  return values.join(' ');
});

console.log(fullName()); // logs "John Doe"

firstName('Mary');

console.log(fullName()); // logs "Mary Doe"

Mithril.js の計算されたプロパティは、アトミックに更新されます。複数のストリームに依存するストリームは、計算されたプロパティの依存関係グラフがどれほど複雑であっても、値の更新ごとに複数回呼び出されることはありません。

ストリームのチェーン ​

ストリームは、map メソッドを使用してチェーンすることができます。チェーンされたストリームは、依存ストリーム とも呼ばれます。

javascript
// 親ストリーム
var value = stream(1);

// 依存ストリーム
var doubled = value.map(function (value) {
  return value * 2;
});

console.log(doubled()); // logs 2

依存ストリームは リアクティブ です。親ストリームの値が更新されると、その値も更新されます。これは、依存ストリームが親ストリームの値の設定前、後どちらに作成されたかに関わらず発生します。

特別な値 stream.SKIP を返すことで、依存ストリームが更新されないようにすることができます。

javascript
var skipped = stream(1).map(function (value) {
  return stream.SKIP;
});

skipped.map(function () {
  // 実行されない
});

ストリームの結合 ​

ストリームは、複数の親ストリームに依存することができます。これらの種類のストリームは、stream.merge() を介して作成できます。

javascript
var a = stream('hello');
var b = stream('world');

var greeting = stream.merge([a, b]).map(function (values) {
  return values.join(' ');
});

console.log(greeting()); // logs "hello world"

または、ヘルパー関数 stream.lift() を使用することもできます。

javascript
var a = stream('hello');
var b = stream('world');

var greeting = stream.lift(
  function (_a, _b) {
    return _a + ' ' + _b;
  },
  a,
  b
);

console.log(greeting()); // logs "hello world"

より高度なユースケースに対応するため、リアクティブな計算においてストリーム自体を公開する stream.combine() という低レベルなメソッドも存在します。

javascript
var a = stream(5);
var b = stream(7);

var added = stream.combine(
  function (a, b) {
    return a() + b();
  },
  [a, b]
);

console.log(added()); // logs 12

ストリームは無数の依存ストリームを持て、アトミックに更新されることが保証されています。例えば、ストリーム A に 2 つの依存ストリーム B と C があり、4 番目のストリーム D が B と C の両方に依存している場合、A の値が変更された場合、ストリーム D は 1 回だけ更新されます。これにより、ストリーム D のコールバックが、B に新しい値があるが C に古い値がある場合など、不安定な値で呼び出されることはありません。原子性は、ダウンストリームを不必要に再計算しないというパフォーマンス上の利点ももたらします。

特別な値 stream.SKIP を返すことで、依存ストリームが更新されないようにすることができます。

javascript
var skipped = stream.combine(
  function (stream) {
    return stream.SKIP;
  },
  [stream(1)]
);

skipped.map(function () {
  // 実行されない
});

ストリームの状態 ​

特定の時点で、ストリームは、保留中、アクティブ、および 終了 の 3 つの状態のいずれかになります。

保留中の状態 ​

保留中のストリームは、引数なしで stream() を呼び出すことによって作成できます。

javascript
var pending = stream();

ストリームが複数のストリームに依存しており、その親ストリームのいずれかが保留中の状態にある場合、依存ストリームも保留中の状態になり、その値を更新しません。

javascript
var a = stream(5);
var b = stream(); // 保留中のストリーム

var added = stream.combine(
  function (a, b) {
    return a() + b();
  },
  [a, b]
);

console.log(added()); // logs undefined

上記の例では、added は保留中のストリームです。これは、その親 b も保留中であるためです。

これは、stream.map を介して作成された依存ストリームにも適用されます。

javascript
var value = stream();
var doubled = value.map(function (value) {
  return value * 2;
});

console.log(doubled()); // `doubled` が保留中のため、undefined をログに記録します

アクティブな状態 ​

ストリームが値を受け取るとアクティブ状態になります(ストリームが終了していない場合)。

javascript
var stream1 = stream('hello'); // stream1 はアクティブです

var stream2 = stream(); // stream2 は保留中で開始されます
stream2('world'); // 次にアクティブになります

複数の親を持つ依存ストリームは、そのすべての親がアクティブな場合にアクティブになります。

javascript
var a = stream('hello');
var b = stream();

var greeting = stream.merge([a, b]).map(function (values) {
  return values.join(' ');
});

上記の例では、a ストリームはアクティブですが、b は保留中です。b("world") を設定すると、b がアクティブになり、したがって greeting もアクティブになり、値 "hello world" に更新されます。

終了状態 ​

ストリームは、stream.end(true) を呼び出すことによって、その依存ストリームに影響を与えるのを停止できます。これにより、ストリームとその依存ストリーム間の接続が効果的に削除されます。

javascript
var value = stream();
var doubled = value.map(function (value) {
  return value * 2;
});

value.end(true); // 終了状態に設定

value(5);

console.log(doubled());
// `doubled` が `value` に依存しなくなったため、undefined をログに記録します

ストリームは依然として状態コンテナとしての性質を保持しています。つまり、終了後でも getter-setter として使用できます。

javascript
var value = stream(1);
value.end(true); // 終了状態に設定

console.log(value(1)); // logs 1

value(2);
console.log(value()); // logs 2

ストリームの終了は、ストリームの有効期間が限られている場合(例えば、DOM 要素がドラッグされている間のみ mousemove イベントに反応し、ドロップされた後は反応しない場合)に役立ちます。

ストリームのシリアル化 ​

ストリームは .toJSON() メソッドを備えています。ストリームが JSON.stringify() の引数として渡されると、ストリームの値がシリアル化されます。

javascript
var value = stream(123);
var serialized = JSON.stringify(value);
console.log(serialized); // logs 123

ストリームはレンダリングをトリガーしません ​

Knockoutなどのライブラリと異なり、Mithril.js ストリームはテンプレートの再レンダリングをトリガーしません。再描画は、Mithril.js コンポーネントビューで定義されたイベントハンドラー、ルートの変更、または m.request 呼び出しの解決に応じて発生します。

他の非同期イベント(例えば、setTimeout/setInterval、websocket サブスクリプション、サードパーティライブラリのイベントハンドラーなど)に応じて再描画が必要な場合は、手動で m.redraw() を呼び出す必要があります。

Fantasy Land とは ​

Fantasy Land は、一般的な代数的構造の相互運用性を指定します。簡単に言うと、Fantasy Land仕様に準拠したライブラリを使用することで、それらのライブラリがコンストラクトをどのように実装しているかに関わらず機能する、汎用的な関数型スタイルのコードを作成できるということです。

例えば、plusOne という汎用関数を作成するとします。単純な実装は次のようになります。

javascript
function plusOne(a) {
  return a + 1;
}

この実装の問題は、数値でのみ使用できることです。ただし、a の値を生成するロジックが、エラー状態(Sanctuary や Ramda-Fantasy などのライブラリからの Maybe または Either でラップされている)、または Mithril.js ストリーム、Flyd ストリームなどを生成する可能性もあります。理想的には、a が持つ可能性のあるすべてのタイプに対して同じ関数の同様のバージョンを作成したくなく、ラッピング/アンラッピング/エラー処理コードを繰り返し記述したくありません。

ここで Fantasy Land が役立ちます。Fantasy Land 代数に関してその関数を書き直しましょう。

javascript
var fl = require('fantasy-land');

function plusOne(a) {
  return a[fl.map](function (value) {
    return value + 1;
  });
}

これで、このメソッドは、R.Maybe、S.Either、stream など、Fantasy Land 準拠の Functor で動作します。

この例は複雑に見えるかもしれませんが、複雑さのトレードオフです。単純な plusOne 実装は、単純なシステムがあり、数値をインクリメントするだけであれば意味がありますが、Fantasy Land 実装は、多くのラッパー抽象化と再利用されたアルゴリズムを備えた大規模なシステムがある場合に、より強力になります。

Fantasy Land を採用するかどうかを決定する際には、関数型プログラミングに関するチームの知識を考慮し、チームがコード品質を維持するためにコミットできる規律のレベル(新しい機能を作成し、締め切りを守るというプレッシャーに対して)に関して現実的である必要があります。関数型スタイルのプログラミングは、小さく正確に定義された関数の大規模な集合のコンパイル、キュレーション、習得に大きく依存するため、堅牢なドキュメント作成習慣を持たないチームや、関数型言語の経験が不足しているチームには適していません。

Pager
前のページcensor(object, extra)
次のページガイド

MITライセンス の下で公開されています。

Copyright (c) 2024 Mithril Contributors

https://mithril.js.org/stream.html

MITライセンス の下で公開されています。

Copyright (c) 2024 Mithril Contributors