Skip to content
Mithril.js 2
Main Navigation AnleitungAPI

Deutsch

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

Deutsch

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

Aussehen

Sidebar Navigation

API

Kern-API

m(selector, attributes, children)

render(element, vnodes)

mount(root, component)

route(root, defaultRoute, routes)

request(options)

parseQueryString(string)

buildQueryString(object)

buildPathname(object)

parsePathname(string)

trust(html)

fragment(attrs, children)

redraw()

censor(object, extra)

Optionale API

stream()

Anleitung

Auf dieser Seite

stream() ​

Beschreibung ​

Ein Stream ist eine reaktive Datenstruktur, ähnlich wie Zellen in Tabellenkalkulationsprogrammen.

Wenn beispielsweise in einer Tabellenkalkulation die Formel A1 = B1 + C1 steht, ändert sich der Wert von A1 automatisch, wenn sich der Wert von B1 oder C1 ändert.

In ähnlicher Weise können Sie einen Stream von anderen Streams abhängig machen, sodass die Änderung des Werts eines Streams automatisch den anderen aktualisiert. Dies ist nützlich, wenn Sie rechenintensive Operationen haben und diese nur bei Bedarf ausführen möchten, anstatt beispielsweise bei jedem Neuzeichnen.

Streams sind nicht im Core-Paket von Mithril.js enthalten. Um das Streams-Modul einzubinden, verwenden Sie:

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

Sie können das Modul auch direkt herunterladen, wenn Ihre Umgebung keine Bündelungs-Toolchain unterstützt:

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

Wenn die Stream-Bibliothek direkt per <script>-Tag geladen wird (anstatt über require), wird sie als window.m.stream verfügbar gemacht. Wenn window.m bereits definiert ist (z. B. weil Sie auch das Haupt-Skript von Mithril.js verwenden), wird sie an das vorhandene Objekt angehängt. Andernfalls wird ein neues window.m erstellt. Wenn Sie Streams in Verbindung mit Mithril.js als reine Skript-Tags verwenden möchten, sollten Sie Mithril.js in Ihre Seite einbinden, bevor Sie mithril/stream einbinden, da mithril andernfalls das von mithril/stream definierte window.m-Objekt überschreiben würde. Dies ist kein Problem, wenn die Bibliotheken als CommonJS-Module verwendet werden (mit require(...)).

Signatur ​

Erstellt einen Stream.

stream = Stream(value)

ArgumentTypeRequiredDescription
valueanyNoWenn dieses Argument vorhanden ist, wird der Wert des Streams auf diesen Wert gesetzt.
returnsStreamGibt den Stream zurück.

Signaturen lesen

Statische Member ​

Stream.combine ​

Erstellt einen berechneten Stream, der reaktiv aktualisiert wird, wenn einer seiner Upstream-Streams aktualisiert wird. Siehe Kombinieren von Streams.

stream = Stream.combine(combiner, streams)

ArgumentTypeRequiredDescription
combiner(Stream..., Array) -> anyYesSiehe Argument combiner.
streamsArray<Stream>YesEine Liste von Streams, die kombiniert werden sollen.
returnsStreamGibt den Stream zurück.

Signaturen lesen

combiner ​

Gibt an, wie der Wert eines berechneten Streams generiert wird. Siehe Kombinieren von Streams.

any = combiner(streams..., changed)

ArgumentTypeRequiredDescription
streams...Splat von StreamsNoSplat von null oder mehr Streams, die den als zweites Argument an stream.combine übergebenen Streams entsprechen.
changedArray<Stream>YesListe der Streams, die durch eine Aktualisierung verändert wurden.
returnsanyGibt den berechneten Wert zurück.

Signaturen lesen

Stream.merge ​

Erstellt einen Stream, dessen Wert ein Array von Werten aus mehreren Streams ist.

stream = Stream.merge(streams)

ArgumentTypeRequiredDescription
streamsArray<Stream>YesEine Liste von Streams.
returnsStreamGibt einen Stream zurück, dessen Wert ein Array von Eingabe-Stream-Werten ist.

Signaturen lesen

Stream.scan ​

Erstellt einen neuen Stream mit den Ergebnissen des Aufrufs der Funktion für jeden Wert im Stream, wobei ein Akkumulator und der eingehende Wert verwendet werden.

Sie können verhindern, dass abhängige Streams aktualisiert werden, indem Sie den speziellen Wert stream.SKIP innerhalb der Akkumulatorfunktion zurückgeben.

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

ArgumentTypeRequiredDescription
fn(accumulator, value) -> result | SKIPYesEine Funktion, die einen Akkumulator und einen Wert als Parameter entgegennimmt und einen neuen Akkumulatorwert desselben Typs zurückgibt.
accumulatoranyYesDer Startwert für den Akkumulator.
streamStreamYesStream, der die Werte enthält.
returnsStreamGibt einen neuen Stream zurück, der das Ergebnis enthält.

Signaturen lesen

Stream.scanMerge ​

Akzeptiert ein Array von Paaren von Streams und Scan-Funktionen und führt alle diese Streams mithilfe der angegebenen Funktionen zu einem einzigen Stream zusammen.

stream = Stream.scanMerge(pairs, accumulator)

ArgumentTypeRequiredDescription
pairsArray<[Stream, (accumulator, value) -> value]>YesEin Array von Tupeln aus Stream- und Scan-Funktionen.
accumulatoranyYesDer Startwert für den Akkumulator.
returnsStreamGibt einen neuen Stream zurück, der das Ergebnis enthält.

Signaturen lesen

Stream.lift ​

Erstellt einen berechneten Stream, der reaktiv aktualisiert wird, wenn einer seiner Upstream-Streams aktualisiert wird. Siehe Kombinieren von Streams. Im Gegensatz zu combine ist die Anzahl der Eingabe-Streams variabel (anstelle eines Arrays), und der Callback empfängt die Stream-Werte anstelle von Streams. Es gibt keinen changed-Parameter. Dies ist im Allgemeinen eine benutzerfreundlichere Funktion für Anwendungen als combine.

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

ArgumentTypeRequiredDescription
lifter(any...) -> anyYesSiehe Argument lifter.
streams...Liste von StreamsYesZu liftende Streams.
returnsStreamGibt den Stream zurück.

Signaturen lesen

lifter ​

Gibt an, wie der Wert eines berechneten Streams generiert wird. Siehe Kombinieren von Streams.

any = lifter(streams...)

ArgumentTypeRequiredDescription
streams...Splat von StreamsNoSplat von null oder mehr Werten, die den Werten der an stream.lift übergebenen Streams entsprechen.
returnsanyGibt den berechneten Wert zurück.

Signaturen lesen

Stream.SKIP ​

Ein spezieller Wert, der an Stream-Callbacks zurückgegeben werden kann, um die Ausführung von Downstream-Streams zu überspringen.

Stream["fantasy-land/of"] ​

Diese Methode hat dieselbe Funktion wie stream. Sie existiert, um die Applicative-Spezifikation von Fantasy Land zu erfüllen. Weitere Informationen finden Sie im Abschnitt Was ist Fantasy Land.

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

ArgumentTypeRequiredDescription
valueanyNoWenn dieses Argument vorhanden ist, wird der Wert des Streams auf diesen Wert gesetzt.
returnsStreamGibt den Stream zurück.

Instanz-Member ​

stream.map ​

Erstellt einen abhängigen Stream, dessen Wert auf das Ergebnis der Callback-Funktion gesetzt wird. Diese Methode ist ein Alias von stream["fantasy-land/map"].

dependentStream = stream().map(callback)

ArgumentTypeRequiredDescription
callbackany -> anyYesEin Callback, dessen Rückgabewert zum Wert des Streams wird.
returnsStreamGibt einen Stream zurück.

Signaturen lesen

stream.end ​

Ein koabhängiger Stream, der abhängige Streams abmeldet, wenn er auf true gesetzt wird. Siehe Beendeter Zustand.

endStream = stream().end

stream["fantasy-land/of"] ​

Diese Methode hat dieselbe Funktion wie stream. Sie existiert, um die Applicative-Spezifikation von Fantasy Land zu erfüllen. Weitere Informationen finden Sie im Abschnitt Was ist Fantasy Land.

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

ArgumentTypeRequiredDescription
valueanyNoWenn dieses Argument vorhanden ist, wird der Wert des Streams auf diesen Wert gesetzt.
returnsStreamGibt einen Stream zurück.

stream["fantasy-land/map"] ​

Erstellt einen abhängigen Stream, dessen Wert auf das Ergebnis der Callback-Funktion gesetzt wird. Siehe Verketten von Streams.

Diese Methode existiert, um die Applicative-Spezifikation von Fantasy Land zu erfüllen. Weitere Informationen finden Sie im Abschnitt Was ist Fantasy Land.

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

ArgumentTypeRequiredDescription
callbackany -> anyYesEin Callback, dessen Rückgabewert zum Wert des Streams wird.
returnsStreamGibt einen Stream zurück.

Signaturen lesen

stream["fantasy-land/ap"] ​

Der Name dieser Methode steht für apply (anwenden). Wenn ein Stream a eine Funktion als Wert hat, kann ein anderer Stream b ihn als Argument für b.ap(a) verwenden. Der Aufruf von ap ruft die Funktion mit dem Wert von Stream b als Argument auf und gibt einen anderen Stream zurück, dessen Wert das Ergebnis des Funktionsaufrufs ist. Diese Methode existiert, um die Applicative-Spezifikation von Fantasy Land zu erfüllen. Weitere Informationen finden Sie im Abschnitt Was ist Fantasy Land.

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

ArgumentTypeRequiredDescription
applyStreamYesEin Stream, dessen Wert eine Funktion ist.
returnsStreamGibt einen Stream zurück.

Grundlegende Verwendung ​

Streams sind nicht Teil der Core-Distribution von Mithril.js. Um sie in ein Projekt einzubinden, binden Sie das Modul ein:

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

Streams als Variablen ​

stream() gibt einen Stream zurück. Auf der einfachsten Ebene funktioniert ein Stream ähnlich wie eine Variable oder eine Getter-Setter-Eigenschaft: Er kann einen Zustand enthalten, der geändert werden kann.

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

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

Der Hauptunterschied besteht darin, dass ein Stream eine Funktion ist und daher zu Funktionen höherer Ordnung zusammengesetzt werden kann.

javascript
var users = stream();

// Nutzer von einem Server mit der Fetch API anfordern
fetch('/api/users')
  .then(function (response) {
    return response.json();
  })
  .then(users);

Im obigen Beispiel wird der users-Stream mit den Antwortdaten gefüllt, wenn die Anfrage abgeschlossen ist.

Bidirektionale Bindungen ​

Streams können auch von Ereignis-Callbacks und ähnlichem gefüllt werden.

javascript
// ein Stream
var user = stream('');

// eine bidirektionale Bindung an den Stream
m('input', {
  oninput: function (e) {
    user(e.target.value);
  },
  value: user(),
});

Im obigen Beispiel wird der user-Stream auf den Wert des Eingabefelds aktualisiert, wenn der Benutzer in die Eingabe tippt.

Berechnete Eigenschaften ​

Streams sind nützlich für die Implementierung berechneter Eigenschaften:

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"

Im obigen Beispiel wird der Wert von slug berechnet, wenn title aktualisiert wird, nicht wenn slug gelesen wird.

Es ist natürlich auch möglich, Eigenschaften basierend auf mehreren Streams zu berechnen:

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"

Berechnete Eigenschaften in Mithril.js werden atomar aktualisiert: Streams, die von mehreren Streams abhängen, werden niemals mehr als einmal pro Wertaktualisierung aufgerufen, unabhängig davon, wie komplex der Abhängigkeitsgraph der berechneten Eigenschaft ist.

Verketten von Streams ​

Streams können mit der map-Methode verkettet werden. Ein verketteter Stream wird auch als abhängiger Stream bezeichnet.

javascript
// Eltern-Stream
var value = stream(1);

// abhängiger Stream
var doubled = value.map(function (value) {
  return value * 2;
});

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

Abhängige Streams sind reaktiv: Ihre Werte werden jedes Mal aktualisiert, wenn der Wert ihres Eltern-Streams aktualisiert wird. Dies geschieht unabhängig davon, ob der abhängige Stream vor oder nach dem Festlegen des Werts des Eltern-Streams erstellt wurde.

Sie können verhindern, dass abhängige Streams aktualisiert werden, indem Sie den speziellen Wert stream.SKIP zurückgeben.

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

skipped.map(function () {
  // wird niemals ausgeführt
});

Kombinieren von Streams ​

Streams können von mehr als einem Eltern-Stream abhängen. Diese Art von Streams kann über stream.merge() erstellt werden.

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"

Oder Sie können die Hilfsfunktion stream.lift() verwenden.

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"

Es gibt auch eine Methode auf niedrigerer Ebene namens stream.combine(), die die Streams selbst in den reaktiven Berechnungen für fortgeschrittenere Anwendungsfälle verfügbar macht.

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

Ein Stream kann von einer beliebigen Anzahl von Streams abhängen, und es wird garantiert, dass er atomar aktualisiert wird. Wenn beispielsweise ein Stream A zwei abhängige Streams B und C hat und ein vierter Stream D sowohl von B als auch von C abhängt, wird der Stream D nur einmal aktualisiert, wenn sich der Wert von A ändert. Dies garantiert, dass der Callback für Stream D niemals mit inkonsistenten Werten aufgerufen wird, z. B. wenn B einen neuen Wert hat, C aber den alten Wert hat. Atomarität bringt auch den Leistungsvorteil, dass Downstream-Streams nicht unnötig neu berechnet werden.

Sie können verhindern, dass abhängige Streams aktualisiert werden, indem Sie den speziellen Wert stream.SKIP zurückgeben.

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

skipped.map(function () {
  // wird niemals ausgeführt
});

Stream-Zustände ​

Zu einem bestimmten Zeitpunkt kann sich ein Stream in einem von drei Zuständen befinden: ausstehend (pending), aktiv (active) und beendet (ended).

Ausstehender Zustand ​

Ausstehende Streams können erstellt werden, indem stream() ohne Parameter aufgerufen wird.

javascript
var pending = stream();

Wenn ein Stream von mehr als einem Stream abhängt und sich einer seiner Eltern-Streams in einem ausstehenden Zustand befindet, befindet sich auch der abhängige Stream in einem ausstehenden Zustand und aktualisiert seinen Wert nicht.

javascript
var a = stream(5);
var b = stream(); // pending stream

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

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

Im obigen Beispiel ist added ein ausstehender Stream, da sein Eltern-Stream b ebenfalls ausstehend ist.

Dies gilt auch für abhängige Streams, die über stream.map erstellt wurden:

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

console.log(doubled()); // logs undefined, da `doubled` ausstehend ist

Aktiver Zustand ​

Wenn ein Stream einen Wert empfängt, wird er aktiv (es sei denn, der Stream ist beendet).

javascript
var stream1 = stream('hello'); // stream1 ist aktiv

var stream2 = stream(); // stream2 beginnt als ausstehend
stream2('world'); // wird dann aktiv

Ein abhängiger Stream mit mehreren Eltern-Streams wird aktiv, wenn alle seine Eltern-Streams aktiv sind.

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

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

Im obigen Beispiel ist der a-Stream aktiv, aber b ist ausstehend. Das Setzen von b("world") würde dazu führen, dass b aktiv wird, und daher würde auch greeting aktiv werden und auf den Wert "hello world" aktualisiert werden.

Beendeter Zustand ​

Ein Stream kann aufhören, seine abhängigen Streams zu beeinflussen, indem stream().end(true) aufgerufen wird. Dadurch wird die Verbindung zwischen einem Stream und seinen abhängigen Streams effektiv entfernt.

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

value.end(true); // in den beendeten Zustand versetzen

value(5);

console.log(doubled());
// logs undefined, da `doubled` nicht mehr von `value` abhängt

Beendete Streams haben immer noch eine Semantik für Zustandscontainer, d. h. Sie können sie weiterhin als Getter-Setter verwenden, auch nachdem sie beendet wurden.

javascript
var value = stream(1);
value.end(true); // in den beendeten Zustand versetzen

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

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

Das Beenden eines Streams kann in Fällen nützlich sein, in denen ein Stream eine begrenzte Lebensdauer hat (z. B. die Reaktion auf mousemove-Ereignisse nur, während ein DOM-Element gezogen wird, aber nicht, nachdem es abgelegt wurde).

Serialisieren von Streams ​

Streams implementieren eine .toJSON()-Methode. Wenn ein Stream als Argument an JSON.stringify() übergeben wird, wird der Wert des Streams serialisiert.

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

Streams lösen kein Rendering aus ​

Im Gegensatz zu Bibliotheken wie Knockout lösen Mithril.js-Streams kein erneutes Rendern von Vorlagen aus. Das erneute Rendering erfolgt als Reaktion auf Ereignis-Handler, die in Mithril.js-Komponenten definiert sind, auf Routenänderungen oder nachdem m.request-Aufrufe abgeschlossen wurden.

Wenn das Neuzeichnen als Reaktion auf andere asynchrone Ereignisse (z. B. setTimeout/setInterval, WebSocket-Abonnement, Ereignis-Handler von Bibliotheken von Drittanbietern usw.) gewünscht wird, sollten Sie m.redraw() manuell aufrufen.

Was ist Fantasy Land ​

Fantasy Land spezifiziert die Interoperabilität gängiger algebraischer Strukturen. Vereinfacht gesagt bedeutet dies, dass Bibliotheken, die den Fantasy Land-Spezifikationen entsprechen, verwendet werden können, um generischen funktionalen Code zu schreiben, der unabhängig davon funktioniert, wie diese Bibliotheken die Konstrukte implementieren.

Nehmen wir beispielsweise an, wir möchten eine generische Funktion namens plusOne erstellen. Die naive Implementierung würde wie folgt aussehen:

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

Das Problem dieser Implementierung ist, dass sie nur mit Zahlen verwendet werden kann. Es ist jedoch möglich, dass jede Logik, die einen Wert für a erzeugt, auch einen Fehlerzustand erzeugen könnte (eingewickelt in ein Maybe oder ein Either aus einer Bibliothek wie Sanctuary oder Ramda-Fantasy), oder es könnte ein Mithril.js-Stream, ein Flyd-Stream usw. sein. Idealerweise möchten wir nicht für jeden möglichen Typ, den a haben könnte, eine ähnliche Version derselben Funktion schreiben, und wir möchten nicht wiederholt Code zum Ein- und Auspacken/zur Fehlerbehandlung schreiben.

Hier kann Fantasy Land Unterstützung bieten. Schreiben wir diese Funktion in Bezug auf eine Fantasy Land-Algebra um:

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

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

Jetzt funktioniert diese Methode mit jedem Fantasy Land-kompatiblen Functor, wie z. B. R.Maybe, S.Either, stream usw.

Dieses Beispiel mag kompliziert erscheinen, aber es ist ein Kompromiss in Bezug auf die Komplexität: Die naive plusOne-Implementierung ist sinnvoll, wenn Sie ein einfaches System haben und nur Zahlen inkrementieren, aber die Fantasy Land-Implementierung wird leistungsfähiger, wenn Sie ein großes System mit vielen Wrapper-Abstraktionen und wiederverwendeten Algorithmen haben.

Bei der Entscheidung, ob Sie Fantasy Land übernehmen sollten, sollten Sie die Vertrautheit Ihres Teams mit der funktionalen Programmierung berücksichtigen und realistisch einschätzen, inwieweit sich Ihr Team zur Pflege der Codequalität verpflichten kann (im Vergleich zum Druck, neue Funktionen zu schreiben und Termine einzuhalten). Die funktionale Programmierung hängt stark von der Zusammenstellung, Pflege und Beherrschung einer großen Anzahl kleiner, präzise definierter Funktionen ab und ist daher nicht für Teams geeignet, die keine soliden Dokumentationspraktiken haben und/oder keine Erfahrung in funktional orientierten Sprachen haben.

Pager
Vorherige Seitecensor(object, extra)
Nächste SeiteAnleitung

Veröffentlicht unter der MIT-Lizenz.

Copyright (c) 2024 Mithril Contributors

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

Veröffentlicht unter der MIT-Lizenz.

Copyright (c) 2024 Mithril Contributors