bd-flux
Version:
a async flow and callback toolkit
269 lines (197 loc) • 10.4 kB
Markdown
[](https://travis-ci.org/rbund/bd-flux)
[](https://www.npmjs.com/package/bd-flux)
bd-flux
=======
(c) 2017 R�diger Bund
Lizenz siehe BSL.TXT oder auch http://www.bundnetz.de/BSL.TXT
(For non German speaking ppl: no English documentation yet)
Was ist bd-flux?
----------------
bd-flux bietet eine L�sung f�r zwei Probleme, es erm�glicht
1. die Steuerung des Kontrollflusses von Javascript-Programmen
2. einen extrem vereinfachten Umgang mit Funktionen die Callbacks bed�rfen
Was unterscheidet bd-flux von anderen Bibliotheken?
---------------------------------------------------
Im unterschied zur anderen Bibliotheken
1. ist bd-flux ein Leichtgewicht, ca. 2K reiner Code ohne Kommentare
2. hat bd-flux keine Abh�ngigkeiten
3. unterst�tzt bd-flux den Umgang mit Callbacks (Scoped Callbacks)
Schnelleinstieg - Beispiel
--------------------------
Grob gesagt erm�glicht bd-flux die Definition von Programmbl�cken, die
nacheinander zur Ausf�hrung gebracht werden. Dies ist notwendig, wenn ein
Programmblock Ergebnisse aus dem vorherigen ben�tigt. Die Besonderheit von
bd-flux liegt darin, dass vor Ausf�hrung des n�chsten Blockes auch alle im
aktuellen Block ausgel�sten Callbacks abgewartet wird.
Dazu ein Beispiel:
var mymessage = "I'm undefined";
function simCallback(error, result, ms, callback) {
setTimeout(() => { callback(error, result); }, ms);
}
function first() {
simCallback(null, "Hello world", 2000, (e,r) => { mymessage = r });
}
function second() {
console.log(mymessage);
}
first();
second();
Bei Ausf�hrung dieses Programmes wird zuerst die Funktion `first()` aufgerufen.
Diese wiederum ruft die Funktion `simCallback()` auf, die nach zwei Sekunden
"zur�ckruft". Inzwischen wird die Funktion `first()`beendet und die Funktion
`second()` vom Hauptprogramm aufgerufen. In dieser wird der Wert der Variablen
`mymessage` auf der Konsole ausgegeben. Zu diesem Zeitpunkt ist der Wert auf
"I'm undefined" gesetzt. Nach zwei Sekunden wird die Callback-Funktion in
`first()` angesprungen und der Wert von `mymessage` auf "Hello world" gesetzt,
jedoch zu sp�t f�r die Ausgabe in `second()`.
Nun das Beispiel �bertragen auf bd-flux:
const fl = require("bd-flux");
var mymessage = "I'm undefined";
function simCallback(error, result, ms, callback) {
setTimeout(() => { callback(error, result); }, ms);
}
function first(data) {
data.result = mymessage;
simCallback(null, "Hello world", 2000, data.__fl().cber());
}
function second(data) {
console.log(data.result);
}
fl().run(first, second);
Zuerst wird die Konstante `fl` auf die eingebundenen Bibliothek bd-flux gesetzt.
Mit dem Aufruf `fl()` wird eine Instanz eines bd-flux Objektes erzeugt und
zur�ckgegeben. Der Aufruf hat den gleiche Effekt wie `new fl()`.
Mit der Methode `run()` werden Funktionen angegeben, die sequentiell ausgef�hrt
werden sollen, gleichzeitig wird mit der ausf�hrung gestartet.
Nun wird der erste definierte Programmblock mit der Funktion `first()`
ausgef�hrt. Als einziger Parameter wird ein Datenobjekt �bergeben. Dieses
Datenobjekt wird von Funktion zu Funktion weitergereicht und ggf. gef�llt.
Initial ist nur eine Variable enthalten, n�mlich eine Referenz auf das aktive
bd-flux Objekt. Der Schl�ssel f�r das Object ist `__fl`. Als erstes setzt
die Funktion `first()` den Wert der Variablen `data.result` auf den Wert der
Variablen `mymessage`. Als n�chstes wird die Funktion `simCallback()` aufgerufen,
als Callback jedoch wird `data.__fl().cber()` �bergeben. Diese Funktion erstellt
eine Callback-Funktion mit zwei Parametern, den ersten f�r einen Fehler (__e__rror),
den zweiten f�r ein Ergebnis (__r__esult) im Erfolgsfall.
Vor der Ausf�hrung `second()` wird nun jedoch auf den Aufruf der
Callback-Funktion gewartet. Das Callback-Ergebnis wird im Datenobjekt abgelegt,
bei einem `cber()` standardm��ig unter den Schl�sseln `error` und `result`.
Da nach Aufruf der Callback-Funktion auf keine weiteren Callbacks gewartet wird,
gilt der Block nun als abgeschlossen und die Ausf�hrung der zweiten Funktion
startet.
In diesem wird der Wert der Variablen `data.result` ausgegeben, der nun korrekt
den Wert "Hello world" beinhaltet.
Referenz
--------
Alle hier genannten Funktion sind Methoden eines bd-flux Objektes.
Parameter in spitzen Klammern sind optional.
ex(funktion<, funktion><,funktion><,...>)
definiert welche Funktionen in welcher Reihenfolge ausgef�hrt werden sollen.
`ex()` kann mehrmalig aufgerufen werden, wobei die in nachfolgenden Aufrufen
genannten Funktionen den vorherigen angeh�ngt werden.
**Kaskadierung**
Aufrufe von `ex()` k�nnen kaskadiert werden. Ein Beispiel:
const fl = require("bd-flux");
function first(data) {
data.__fl.ex(third, fourth);
}
function second(data) {
...
}
function third(data) {
...
}
function fourth(data) {
...
}
fl().ex(first, second).run();
Bei dieser Verkettung wird zuerst `first()` aufgerufen, danach `third()`,
dann `fourth()` und zuletzt `second()`.
run(<funktion><, funktion><,funktion><,...>)
Startet die Ausf�hrung. Optional k�nnen analog zu `ex()` auszuf�hrende Funktionen
benannt oder hinzugef�gt werden.
d(<schl�ssel><wert>)
`d()` arbeitet je nach Parameteranzahl unterschiedlich. Bei keinem Parameter wird
als Ergebnis das Datenobjekt zur�ckgegeben. Bei Angabe nur eines Parameters wird
dieser als Schl�ssel interpretiert und der dazu gespeicherte Wert aus dem
Datenobjekt zur�ckgegeben. Bei zwei der mehr Parametern werden (momentan) nur die
ersten zwei ber�cksichtigt, der erste als Schl�ssel und der zweite als Wert. Dieses
Paar wird im Datenobjekt abgelegt.
cb(<name><,name><,name><,...>)
Gibt eine Callback-Funktion zur�ck, die die ihr �bergebenen Parameter im Datenobjekt
speichert. Die �bergebenen Parameter <name> werden als Schl�ssel f�r das Datenobjekt
interpretiert. Die Callback-Funktion �bernimmt die Zuordnung der Ergebnisse zu den
evtl. zuvor angegebenen Schl�sseln. Dabei gibt es zwei Szenarien:
1. Es wurde ein Schl�ssel f�r mehrere Ergebnisse angegeben
2. Es wurde f�r f�r jedes Ergebnis ein Schl�ssel angegeben
Fall 1 tritt dann ein, wenn mehrere Ergebnisse aber genau nur ein Schl�ssel
vorhanden ist. In dem Fall werden alle Ergebnisse als Array zum Schl�ssel im
Datenobjekt abgelegt. Gibt es nur einen Schl�ssel und nur ein Ergebnis, so wird das
eine Ergebnis direkt zum Schl�ssel gespeichert. Wenn es mehrere Schl�ssel gibt,
so werden die Ergebnisse in der gleichen Reihenfolge der Schl�ssel zugeordnet.
�bersch�ssige Ergebnisse, f�r die kein Schl�ssel definiert wurde, werden ignoriert.
Falls mehr Schl�ssel als Ergebnisse vorhanden sind, werden die Werte der �bersch�ssigen
Schl�ssel mit `undefined` gef�llt.
cber(<name>)
Gibt eine Callback-Funktion zur�ck, die zwei Parameter aufnimmt, den ersten f�r den
Fehler- und den zweiten f�r den Erfolgsfall. Optional kann der Schl�ssel, unter dem
das Ergebnis im Erfolgsfall im Datenobjekt gespeichert wird, angegeben werden.
Sollte dieser nicht agegeben sein, so wird der Schl�ssel auf "result" gesetzt.
Ist ein Fehler aufgetreten, so wird dieser mit dem Schl�ssel `error` im Datenobjekt
abgelegt. Wenn der Fehler nicht gesetzt ist, ist vom Erfolgsfall auszugehen und das
Ergebnis wird wie zuvor definiert abgespeichert.
sjob(array, funktion, resultkey)
pjob(array, funktion, resultkey)
Um eine Datenreihe, repr�sentiert durch einen Array, mit nur einer Funktion
abzuarbeiten, werden zwei Methoden zur Verf�gung gestellt, die "job"-Methoden.
Sie unterscheiden sich darin, dass eine die Daten sequentiell, also nacheinander
abarbeitet, w�hrend die zweite dies parallel tut. Der benannten Funktion werden
zwei Parameter �bergeben, der erste ist das array-Element, der zweite wie zuvor
das Datenobjekt. Die Ergebnisse der Abarbeitung liegen unter dem im `resultkey`
spezifizierten Schl�ssel im Datenobjekt als Objekt.
WICHTIG: das Ergebnis ist nicht als Ergebnis eines R�ckgabewertes zu verstehen,
sondern eine Sammlung der Datenobjekte jeder Ausf�hrung der Iterationsfunktion.
Die Ergebnisse m�ssen in der Iterationsfunktion im �bergebenen Datenobjekt
abgelegt werden! Das klingt komisch, hat aber den Vorteil, dass in der
Iteration auf Callbacks gewartet und eigene Abl�ufe von bd-flux Objekten
ausgef�hrt werden kann.
Ein Beispiel:
const fl = require("bd-flux");
function random(x) { return(Math.floor(Math.random() * x)); }
function test1_1(d) {
var data = []
for (var i = 0; i < 20; i++) data.push(i*5);
d.a = data;
console.log('test1_1 passed');
}
function test1_2(d) {
var a = function(v, cb) {
setTimeout(function() {
if (isNaN(v)) cb('error, v is not a number');
else cb(null, v);
}, (random(15)+1) *100);
},
b = function(element,d) {
a( element * 10, d.__fl.cber('key'));
};
d.__fl.sjob(d.a, b, 'jobresult');
console.log('test1_2 passed');
}
function test1_3(d) {
console.log('test1_3, job results:', d.jobresult.key);
console.log('1_3 passed');
}
fl().run(test1_1, test1_2, test1_3);
console.log('test1 passed');
Beispielende.
sub()
Es kommt vor, dass es sinnvoll ist im aktuellen Ablauf einen neuen zu erstellen,
speziell z.B. w�hrend der Ausf�hrung von jobs.
Dies ist jederzeit m�glich indem eine neue Instanz eines bd-flux Objektes
erstellt wird. Wird der Ablauf einer neuen Instanz gestartet, l�uft er "unabh�ngig"
vom aktuellen, d.h. potentiell parallel ab. Kniffliger ist es, den neuen Ablauf mit
dem aktuellen zu synchronisieren. Hierf�r ist die Methode `sub()` gedacht. Sie
erstellt ein neues bd-flux Objekt und gibt dieses zur�ck. Die aktuelle Funktion wird
allerdings erst dann abgeschlossen, wenn zuvor der per `sub()` angeforderte neue Ablauf
ausgef�hrt wurde.
(Fortsetzung folgt)