Launchpad.JS - Ein JavaScript TaskManager

Einleitung

Das Web funktioniert nach wie vor Page-Based. Das war vor vielen Jahren auch ausreichend. Eine Seite wird erzeugt, übertragen und dargestellt. Es gibt zahlreiche Administrationsoberflächen, die nach diesem Schema funktionieren: Der Benutzer von Administrationsoberflächen kann Informationen editieren und über einen Submit-Button diese zurück an den Server senden.

Heutzutage entspricht dieses Vorgehen in den allerwenigsten Fällen unserer Vorstellung einer flüssigen Applikation. Eine Bearbeitung auf diese Weise ist zeitaufwendig und bei den Mitarbeitern unbeliebt. Dies führt zu einer inakzeptablen Produktivität und kurz- bis mittelfristigem Austausch der entsprechenden Applikation lange vor einem Return Of Invest.
Als Softwarestudio ist es unsere Aufgabe dem Kunden ein Tool an die Hand zu geben, das sich lohnt zu entwicklen. Übersicht und Schnelligkeit zählen. Ein wenig Vordenken ist gefragt, um ein Produkt zu erzeugen, das heute beeindruckt und mindestens morgen noch überzeugt. Qualität findet sich im Detail wieder. In der UX-Gestaltung genauso wie in den appliaktiven Grundlagen fachlicher Anforderungen.

Um die Einschränkungen der Webtechnologien so zu nutzen, dass unsere heutige Vorstellung von Applikationen abzubilden ist, hat sich ein eventgetriebenes Verfahren durchgesetzt. Die Technologie von modernen Webservern und Request-Clients ermöglichen es kleine Daten-Fragmente öfter und schneller hin und her zu senden, um dadurch einen Kommunikations-Strom zu erzeugen. Dies lässt die Seitenbasierung in einer Geschwindigkeit ablaufen, die uns den Eindruck einer kontinuierlich laufenden Unterhaltung vermittelt. Vergleichbar mit dem Verhältnis vom Foto zum Film. Laufen die Einzelbilder schnell genug ab entsteht für uns der Eindruck einer gleichförmigen Bewegung. 

Übertragen wir hin und wieder ein Keyframe zur Synchronisation, lässt sich der Rest aus berechneten Veränderungen füllen. Was uns Netflix im Privaten so charmant macht lässt, sich auf alltägliche Aufgaben zur Administration unserer Geschäftsanwendungen übertragen. 

Zur Datenübertragung haben sich in den letzten Jahren eine Vielzahl an technischen Varianten entwickelt. XML als RPC, SOAP als Geschmacksrichtung und letztendlich JSON, und die Entwicklungen der Clientbibliotheken ziehen mit: WebSockets, HTTP/2…

LaunchpadJS-Rocket

04.01.2017 - javascript ux taskmanager opensource

aus der Technik setzt Kundenprojekte unter Anderem mit JavaScript-Frameworks um. Wir legen hohen Wert auf Sicherheit, Performance und Ergonomie. Zahlreiche Administrationsfrontends helfen unseren Kunden bei der täglichen Arbeit. Eine Webapplikation misst sich an der Freude der Bedienung. Wir stehen auf den Schultern von großartigen, offenen Implementationen wie BackboneJS, Underscore und Async. Ohne diese Frameworks wäre es nicht möglich professionelle Applikationen für unsere Kunden zu einem akzeptablem Preis umzusetzen. Die Anwendung von Designpatterns schafft wiederholbare Struktur und lässt mehr Zeit für die Optimierung der Bedienelemente.

Die IT-Welt verändert sich ständig. Mit jeder Entwicklung kommen neue Anforderungen hinzu. Mechaniken überholen sich, Paradigmen wechseln und Szenen beeinflussen sich gegenseitig. In dieser Wechselphase gibt es immer verbesserungsfähige Tools, die erst von einer Technologie in die andere transferiert werden müssen.

aus der Technik kommen von Zeit zu Zeit Grundlagenentwicklungen, die helfen Projekte besser und einfacher umzusetzen. Diese Entwicklungen teilen wir mit anderen Entwicklern und Firmen als OpenSource, frei jeglicher einschränkender Lizenz und in der Hoffnung, dass unsere Zeit und unsere Gedanken bei interessanten Projekten helfen.

Zeit, die aus der Technik in Grundlagenentwicklung steckt, kann und wird heutzutage nicht mehr vom Projektgeschäft bezahlt. Es ist jedoch wichtig sich über Tools und Vorgehensweisen auszutauschen und wiederkehrende Aufgaben mit Verstand und Sorgfalt richtig zu lösen. Es ist Grundlage von Umsetzungsprojekten auf solide Basiskomponenten aufzusetzen, die dem jeweiligen Projektteam angeboten werden. 

Wir werden deshalb anfangen unsere OpenSource Projekte auf GitHub (https://github.com/aus-der-Technik) zu veröffentlichen. 

Problematik

Nach einigen Ausflügen in diverse MVC’s, MVVC’s Key-Value-Binders, View-Hirarchies und Template-Engines haben wir unser Hauptaugenmerk auf BackboneJS gerichtet. Der Rahmen gibt uns für die Umsetzung genügend Freiheiten. Wir erachten den wohlbalancierten Spielraum als förderlich, um komplexe Anwendungen mit einem simplifizierten Frontend zu gestalten. 

Der modulare Aufbau der Applikationen ermöglicht es wiederkehrende Aufgaben in eigenen Teilprojekten in unserem Firmenrepository zu halten. Tests und Buildsysteme fügen die Teile zusammen. Paketsysteme organisieren die Benutzung in den Zielprojekten. "Abstraction By Design" führt zu kontrollierbareren Softwareständen, die im Team geteilt werden können.

BackboneJS an sich gibt einer Webapplikation Struktur und stellt Model-Objekte zur Verfügung, auf denen der Projektcode agieren kann. Der Sync-Mechanismus kapselt die Kommunikation mit den API-Backendservern sauber ab.

In zahlreichen Best-Practises und durch die Beratungsarbeit in Unternehmen haben wir verschiedene maßgeschneiderte Lösungen entwickelt, und dabei eine Codebasis vor allem durch Abstraktionen geschaffen, die BackboneJS durch sein hervorragendes extend-modell und Eventsystem erlaubt. 

In einem Projekt haben wir den Speicher-Button als Überbleibsel der seitenbasierten Applikationen des Webs wegabstrahiert. Änderungen an Datensätzen werden sofort und unmittelbar im Hintergrund übertragen. Die Herausforderung bestand darin auf die Serverantworten zu reagieren, auch wenn der User sich zu dieser Zeit nicht mehr in dem ursprünglichen Scope befand. Der Kommunikationsmechanismus musste in abstrakte Views gekapselt werden. Große Projekte, die durch Continious Integration und agile Methoden ein hohes Maß an Veränderungen erfahren, bleiben so extrem pflegbar, da durch die Abstraktion der eigentliche Implementationscode lesbar, wartbar und vor allem für jeden Projektbeteiligten zu verstehen bleibt. 

In dem Projekt definierten wir die Views nicht mehr daran wann sie Ihre Modell-Daten laden und zurück schreiben, sondern machten dies implizit von der jeweiligen Benutzeraktion abhängig. Das setzt voraus, dass ein Timingmodell zugrunde liegen muss, in dem Nebenläufigkeiten entweder nicht auftreten können, oder einfach zu debuggen und zu beheben sind. 

Diesen Maßnahmen vorausgesetzt ist eine intensive Auseinandersetzung mit den Prinzipien des projektbezogenen UX-Designs. Nicht mehr der Code schreibt dem Anwender vor, wann er welche Aktion ausführen muss, sondern der Benutzer löst Events aus, die eine Verarbeitung anstoßen. Für den Fluß einer Applikation bedeutet dies, das mehrere Handlungsstränge aufgemacht werden können, die letztendlich alle behandelt und positiv geschlossen werden müssen. Dabei soll der Benutzer so wenig wie möglich vom System “belästigt” werden. In der Praxis bedeutet das jedoch für den Entwickler, das systembedingt neue Handlungsstränge entstehen können, sobald Abweichungen vom wahrscheinlich zu erwartenden Ergebnis auftreten. Beispiele hierfür sind serverseitige Fehler, unzulässige Eingaben, und abhängige Resultate von parallel existierenden Requests. 

Zu Lösen sind diese mit Ausnahmebehandlungen, die je nach Aufgabe durch Callbacks, Delegates oder Injections gelöst werden können. 

Für Anwendungen im reinen Datenerfassungs- und Bearbeitungsbereich ist das absolut handhabbar. Ist der Benutzer der einzige Akteur können Designpatterns helfen die Events in ein für ihn positives oder zumindest einleuchtendes Ergebnis aufzulösen. In den letzen Jahren wurde großes Augenmerk darauf gelegt ein tolles Nutzererlebnis zu schaffen. Viele Frameworks und Libraries sind entstanden, die dabei helfen diese Aufgaben zu erledigen und in möglichst viele Anwendungen einfließen zu lassen. Alle modernen Frameworks bieten eine Möglichkeit diese Erwartungen an eine Administrationsoberfläche zu erfüllen. BackboneJS vor allem durch die Erweiterung der Sync-Methoden und des Eventsystems. Wann immer etwas geschehen soll kann der Programmierer sich in die wohlgeschriebenen Funktionen einhängen und diese manipulieren. 

In diesem Vorgehen ist die Prozessmodellierung frei gestaltbar. Da es sich jedoch um den wichtigesten Teil handelt, der in großen Projekten modularisiert, testgetrieben und mehrfach verwendet wird, sollte die Implementation unabhängig von der View definiert und ausgeführt werden können. Das trifft besonders dann zu, wenn die Prozesse durch externe Events manipuliert werden können, oder in einer großen Abhängigkeit zueinander stehen. 

Um dieses Vorgehen zu modularisieren stellen sich mehrere Fragen an das UX-Design. Was erwarten wir vom Web und von einer guten Administrationsoberfläche? Was machen Andere gut und wie funktionieren die Dinge auf anderen Plattformen? Wie sehen die Anforderungen aus und wie können wir Mehrwerte schaffen, die Bedürfnisse unserer Kunden befriedigen und ihnen zum Erfolg verhelfen? Die Bedarfsanalyse zeigt, dass das obige Modell soweit fein ist, jedoch der Akteur nicht der alleinige Initiator der zu behandelnden Events ist. Interaktion stoppt nicht, sind die Daten einmal beim Benutzer. Interaktion wartet nicht auf die alleinige Aktion des Akteurs. Vielmehr ist er Teil einer Initiatorenkette als gleichberechtigtes Mitglied neben anderen Usern im Backend, neben sich verändernden Daten und ihren Zuständen - eben Echtzeitszenarien und Nebenläufigkeiten. 

Das ist soweit ein länger bekanntes und zum Teil gelöstes Problem. Bei derartigen Fragestellungen lohnt ein Blick zu kollaborativen Tools wie Etherpad, Google Docs und iCloud. Aber auch der Blick auf moderne Desktop-Applikationen im Vergleich zu Administrationsfrontends zeigt, dass im Web die serielle Abarbeitung der Prozesse den User als ein Baustein dieser Kette sieht, statt diese von ihm abhängig zu machen. Als einfaches Beispiel sei die bestätigende Aktion eines Button-Clicks angeführt. Der Button wird gedrückt  der Client sendet die Daten an den Server, der Server antwortet und der Benutzer bekommt gegebenenfalls eine Fehlermeldung dargestellt. Unsere Beobachtungen in den letzten Jahren sind die, dass im Desktop-Bereich eine Hintergrundaktualisierung von diesen Prozessen zum Glück immer mehr zum Standard wird, während das Web und insbesondere Administrationsoberflächen bei dem Thema noch viele Lücken aufweisen. 

Zwar können wir mit BackboneJS (und anderen Frameworks) gut auf alle Events reagieren, jedoch nimmt die Komplexität mit jeder Eventualität zu. Es entsteht ein Handlungsbedarf in der Abstraktion der Abarbeitung von Aufgaben-Funktionen. Ist eine klare Aufgabe gut mit den Onboard-Mitteln abzubilden, schleichen sich schnell schwierig zu debuggende Fehlerquellen ein, sobald eine Eventualität hinzukommt. Zusätzlich muss diese klassischerweise in alle Views und Models nachimplementiert werden. Will man komplexere Zusammenhänge abbilden, die sich aus mehreren Datenstreams ergeben, endet der JavaScript-Code schnell in massiven Callback-Höllen. Ein Wegabstrahieren in Async-Wrappers, Promisses und Queues verlangt ein hohes Maß an Verständnis der Technologie und eine besondere Sorgfalt im Schreibstil des Projektcodes. Wir erleben immer wieder, dass gerade in Bezug auf einen Sync-Call die Views so aufgebaut werden, dass das Admininterface unnötig blockiert wird. Durch unleserlichen Code beschäftigen wir uns wiederholt mit der Erklärung von Nebenläufigkeit. Das gilt besonders in Projekten mit starker Kommunikation zu REST-Servern. 

Wir erachten es als problematisch, wenn im Code Patterns zur Abarbeitung in der gleichen Datei ausformuliert werden wie die Businesslogik. Wie etwas verarbeitet wird und was verarbeitet wird sollte nach unseren Erfahrungen getrennt werden, um eine Übersicht zu schaffen, die auch in großen Projekten pflegbar bleibt. Die Prozessdefinition klar strukturiert zu halten hilft bei der Bewältigung komplexer Aufgaben.

Wir vermissen eine super simple API, um Prozesse zu definieren und an einen Handler zu übergeben. Ein Taskmanager für browserbasiertes JavaScript.

Ein Queue-TaskManager für Javascript: LaunchpadJS

Aus dieser langen Überlegung und Auseinandersetzung mit der Umsetzung von Anforderungen in sauberen Programmcode ist ein einfaches Modell entstanden. Ein zu erstellender TaskManager soll zwischen unserem Clientcode und den Framework-Komponenten tätig werden. Wir möchten Aufgaben als Funktionen beschreiben können, die in einem jeweils für eine Überaufgabe geteilten Scope laufen, um Daten untereinander auszutauschen. Getrennte Aufgabenbereiche sollen keinen Zugriff auf Daten untereinander erhalten. Es muss möglich sein diese Definitionen aus verschiedenen Kontexten heraus zu definieren und dem Manager zu übergeben. Dieser soll die Aufgaben, wenn sie fertig zusammengestellt sind, abarbeiten. Eine Abarbeitung der einzelnen Aufgaben muss gemonitored werden können, um auf Veränderungen zu reagieren. Beispielsweise muss eine View jederzeit den Status einer Aufgabe und deren Rückgabewerte anzeigen können. Es ist wichtig für das Frontend darüber informiert zu werden, wann ein Task mit der Abarbeitung anfängt, und wann dieser beendet ist. Metriken über Geschwindigkeiten und Fehlerverursacher müssen erstellt werden können, ohne dabei in die Kernaufgabe des Taskmanagers eingreifen zu müssen. Demnach benötigen wir ein Event-Modell. Aufgaben können weitere Eventualitäten entwerfen und Reaktionsanweisungen der auszuführenden Prozesskette anhängen. Das heisst eine eingestellte Aufgabe kann bei ihrer Abarbeitung weitere Aufgaben aus sich heraus erzeugen und hinten an die Ausführungskette anhängen. Beim Start einer Ausführungskette wissen wir demnach nicht wann diese beendet sein wird, dennoch muss über das Event-Modell auf die Zustände reagiert werden können um Änderungen am DOM zur rechten Zeit vornehmen zu lassen. Es wird elementar wichtig  die Nebenläufigkeit zu vermeiden, auch auf Kosten der Performance, solange das Userinterface niemals blockiert. Wir möchten für JavaScript, was NSOperation für Cocoa ist erstellen. 

Eine beispielhafte Anwendung

Anwendungsbeispiel
 

Eine Zahnpastafirma bekommt ein Investment Kapital um auf den Hype der Industrie 4.0 Produktion aufzuspringen. Kunden sollen sich künftig eine eigene Zahnpasta zusammenstellen können. Unternehmensfarben sollen sich in dem Produkt wiederfinden. Die Produktionsanlagen sind dafür seit Längerem vorbereitet, das Marketing hat sich für einen begleitenden Prozess entschieden. Zunächst sollen Hotels und Zahnärzte als B2B Kunde angesprochen werden. Durch den begleitenden Prozess sollen Kunden online durch die verschiedenen Schritte geleitet werden. Nun liegt es an uns Entwicklern dafür ein geeignetes Frontend bereitzustellen. 

Im Katalog liegen verschiedene Einzelteile wie Tuben, Spender, Kappen, aber auch Farbstoffe und Geschmacksrichtungen. Auf einer Plattform wählt der Kunde seine gewünschte Grundform aus und einem freien Mitarbeiter der Zahpastafirma wird in einem Konfigurator ein Chat mit diesem Kunden geöffnet. Der Mitarbeiter kann nun verschiede Teile auswählen und in einem Produkt kombinieren. Dieser Prozess muss möglichst einfach sei. Nicht herrstellbare Konfigurationen sollen mit Alternativen ausgeglichen werden. Der Kunde wird aktiv in diesen Prozess eingebunden und erhält, sofern das Produkt realisierbar ist, eine Auswahlmöglichkeit in seinem Browser zur Gestaltung der Streifendicke, der Farben, der möglichen Geschmacksrichtungen, u.s.w. 

Für den Kunden soll das Design sehr einfach sein und sich in Schritten durch den Prozess hangeln. Der Mitarbeiter besitzt ein etwas komplexeres Frontend, in dem er zu jeder Zeit sowohl das Produkt mit allen Einzelteilen konfigurieren kann, Alternative Teile ersetzen kann, Hinweise zu einer möglichst schnellen und günstigen Produktion bekommen, aber auch den Kunden und den Chatverlauf im Focus behält.

Das Lager wurde erst neulich mit einem REST-Server als Layer über dem vorhandenen Lagerverwaltungssystem ausgestattet. An diesem System soll das Forntend des Mitarbeiters gleich prüfen, welche Teile vorhanden sind und diese ggf. zur Nachbestellung markieren. Sobald das Produkt in Kooperation zwischen Mitarbeiter und Kunden fertig konfiguriert ist, werden die benötigten Einzelkomponenten im Lager reserviert. Der Kunde erhält (sofern Komponenten nachbestellt werden müssen) eine Aufstellung über den vermutlichen Produktionsbeginn, wobei der Mitarbeiter durch die Möglichkeiten des Austausches von Einzelkomponenten eine Alternative konfigurieren kann. Diese Alternative kann er dem Kunden vorschlagen und wird somit zum persönlichen Berater. 

Ist eine Konfiguration abgeschlossen muss diese vom Mitarbeiter, aber auch vom Kunden freigegeben werden. Das dritte Systeme wird nun angesprochen und der Auftrag wird in die Produktion gegeben. Der Mitarbeiter kann sich im Chatverlauf verabschieden, während der Kunde eine Übersicht über die Produktion erhält.

 

Das ist keine Aufgabe, die sich nicht mit den Boardmitteln von BackboneJS oder anderen Frameworks wie Angular oder React abbilden ließe, doch bleibt die Implementation unsauber und teilweise durch Nebenläufigkeit nicht mehr nachzuvollziehen, wenn hierbei Prozessabhängigkeiten nicht von der Prozessdarstellung gelöst werden. Ferner findet die Definition in Async oder Promises Ketten statt und muss in der View selber oder deren abstrakten Elternobjekten definiert werden. 

Eine "View" besitzt nicht mehr nur ein Model-Object und muss auf Events mehrerer Kanäle angemessen reagieren können, ohne dabei die momentan ausgeführte Arbeit des Mitarbeiters zu torpedieren. Als reine Frontendapplikation wird es schwierig einen pflegbaren Code zu erhalten, wenn requests und responses zu den einzelnen Systemen nicht ausgelagert in einer dedizierten Abarbeitungskette liegen. Darüber hinaus gibt es zu einer Aktion nicht mehr die eine definierte Abfrage an den Server. Abfragen bedingen sich und können Folgefragen erzeugen. In unserem Beispiel liegen Katalog und Lagerbestand in zwei Systemen. Wir verzichten auf Grund der Projektvereinfachung auf eine Middleware und müssen daher den trade-off mehrerer Abfragen aus dem Frontend heraus in Kauf nehmen. 

Gerade in einem B2B-Proove ändern sich die Prozessdefinitionen mit Fortbestehen des Angebotes im Sinne des KVP. Das Frontend muss auf diese Änderungen reagieren können. Ein Projekt im dreistelligen Tausenderbereich muss sich tragen und mit den Anforderungen wachsen. Es ist daher von Vorteil wenn die Prozessschritte und deren Regelbedingungen separat von einander im Programmcode definiert werden können. 

Das bei dem Beispiel ein großer Teil der sonst üblichen Backend-Funktionen in das Frontend verlagert werden hängt stark mit den Interaktionsmodellen zusammen. Es ist die kürzeste alle Möglichen Request-Response-Cyclen zu wählen, um ein wirklich flüssiges Erlebnis zu erzeugen. Der für dieses Projekt eigens eingeführte REST-Server kümmert sich hingegen um die elementaren Betriebsstabilisationen des Systemes wie State-Cache, User-Storage um Zwischenstände zu speichern, Authentifikation und andere grundlegende Funktionalitäten. 

Wir möchten eine API haben in der wir verschiedene Scopes aufmachen können in denen parallel Aufgaben (Funktionen) abgearbeitet werden. Wir möchten über Änderungen im Folgenden informiert werden. Wir möchten die Reihenfolge der Tasks bestimmen können, ohne dabei den jeweiligen Schreibstil des Entwicklers zu sehr zu beeinflussen. 

Die Aufgabe die wir uns stellten war es einen Taskmanager für JavaScript zu erzeugen, dessen Basisfunktionen die oben beschriebenen Probleme lösen. Die Art der Abarbeitung soll in einer Komponente beschrieben werden und sich einfach in bestehende BackboneJS Views integrieren lassen. Zudem soll der komplette Code mit UnitTests versehen sein um zu gewährleisten, dass eine Abarbeitung der Aufgabenkette vorhersagbar ist.

Einfachheit

Das Prinzip des TaskManagers aus der Technik ist so einfach, dass uns eine Veröffentlichung fast schon als zu trivial erschiene, wären die Auswirkungen in den Projekten nicht so groß. Diskussionen um aktuelle Projekte und die Analyse der Codebasen zeigen, dass diese Grundlagenentwicklung interessant für unsere Freunde und Partner sein kann, da sich für uns eine Grundproblematik durch die Arbeit mit dem TaskManager löst. Wir hoffen, dass der Gedankengang dazu anregt komplexe Aufgaben durch konzeptionelle Arbeit einfacher abbilden zu können. 

In Projekten, deren Frontend-Code kritische Teile der Prozessmodelierung beinhalteten muss dieser einerseits unabhängig von der View-Mechanik, anderseits jedoch vom Design und der Benutzerführung her gedacht werden. Zunächst muss herausgestellt werden, wie die Benutzerführung funktionieren soll, daraufhin können technische Implementationen entwickelt werden, welche von den Prozessen angesteuert werden.

Wir beziehen uns im Folgenden auf einen Proove of Concept, der bestimmte Annahmen macht, die nicht im Endprodukt zu finden sein werden. Zur Verdeutlichung gehen wir von drei parallelen Prioritätskanälen aus. Das ist aufgrund der Requestimplementation und Limitierung verschiedener Browser so nicht sinnvoll. Für den PoC und die Erklärung hier jedoch ein Vorteil. Eine spätere Veröffentlichung wird auf Sonderfälle und die verschiedenen Runtimes dynamisch reagieren.

Wir nennen die Prioritäten: 

  • HIGH 
  • MID 
  • LOW 
Jede Priority hat einen Executor, in dem ein Task ausgeführt werden kann. Verschiedene Tasks teilen sich eine gemeinsame Data-Variable, wir nennen diese Namespace. 
Eine Leere Queue

 

Tasks können sich einen Namespace teilen (im Bild mit unterschiedlichen Farben dargestellt) und auf verschiedenen Prioritäten liegen.

Mit Tasks befüllte Queue

 

Im Bild gibt es 8 Tasks in 4 Namespaces auf 3 Prioritäten, die abgearbeitet werden sollen. Der TaskManager folgt nun drei einfachen Regeln: 

  • Eine kleinere Indexnummer wird immer vor einer niedrigeren Priorität ausgeführt 
  • Tasks in einem Namespace werden immer sequenziell ausgeführt 
  • Superlocks werden durch einfaches Drehen der ersten beiden Tasks nach Reihe der Prioritäten aufgelöst 
Das Video zeigt die Reihenfolge der Abarbeitung. Es ist zu beachten, das Parallelität hierbei noch nicht visuell berücksichtigt wurde: 

Launchpad.JS

API

Ein TaskManager muss einfach zu benutzen sein. Das Design der API ist deshalb entscheidend. Die folgenden Beispiele beschreiben wie drei Tasks in zwei unterschiedliche Namespaces gesetzt und zur Ausführung gebracht werden.

Zunächst erzeugen wir uns drei Task-Funktionen:

var tueDies = function(scope, next){
    scope.result = “Hello”;
    next();
}

var tueDas = function(scope, next){
    scope.result += “World”;
    next();
}

var tueJenes = function(scope, next){
    scope.result = tueDies(scope, function(){
        scope.result += “Mars”;
    });
    next();
}
Diese übergeben wir nun dem TaskManager, um sie auszuführen. 
var tm =  taskmanager.instance();
Der TaskManager ist ein Singleton Object, das mit der einzigen Funktion instance() geholt werden kann.  
var nsEarth = tm.createNamespace('earth');
var nsMars = tm.createNamespace('mars');
Mit createNamespace, einer Funktion des TaskManagers, lassen sich Namespaces erzeugen. In diese lassen sich nun Tasks einhängen. 
var t1 = tm.addTask(nsEarth, tm.priority.BACKGROUND, tueDies);
var t2 = tm.addTask(“earth”, tm.priority.IMMEDIATELY, tueDas);
var t2 = tm.addTask(nsMars, tueJenes);
 

Ein Task kann einem Namespace durch die Übergabe des Namespace-Objektes oder durch die Angabe des Namens des Namespaces als String übergeben werden. Ebenso ist die Priority-Chain optional. (Spätere Implementationen sollten diese dann sinnvoll aufteilen, statt fix dem FOREGROUND zu übergeben.)

Beide Namespaces müssen nun noch gestartet werden:

nsEarth.start();
tm.getNamespace(“mars”).start();
Auch hier ist wieder die Aktion auf den Namespace selber anzuwenden. Es ist auch möglich, den Namespace über getNamespace per String zu holen und direkt auf den Rückgabewert zu agieren. Da der Taskmanager ein Singleton ist lassen sich kontextlos Aufgaben zu bestimmten Namespaces zuordnen.  
 
Die Ausführung des Programmcodes kann nahtlos weitergehen, während sich der TaskManager um die Abarbeitung der Tasks kümmert. Zum Grundset des PoC gehören weitere Namespace-Funktionen:  
  • start() - Startet des Namespace und damit alle seine Tasks in ihm 
  • abort() - Stoppt den Namespace und verwirft alle noch nicht gelaufenen Tasks 
  • halt() - Pausiert den Namespace nach der aktuellen Abarbeitung des laufenden Tasks 
  • resume() - Lässt einen pausierten Namespace weiterlaufen 
Um nun an die Ergebnisse zu kommen können dem Namespace Funktionen übergeben werden. Fünf Schnittstellen finden sich im PoC: 
  • onStart(fn()) - Wenn der erste Task im Namespace ausgeführt wird 
  • onFinish(fn()) - Wenn der letzte Task im Namespace ausgeführt wird 
  • onError(fn(err)) - Wenn der Task einen Error meldet 
  • beforeTask(fn())  - Bevor ein neuer Task ausgeführt wird 
  • afterTask(fn()) - Nachdem ein Task ausgeführt wurde 
Endergebnisse lassen sich nun über die onFinish() Funktion abfragen. Das geteilte Datenobjekt wird dabei im Callback zurückgegeben. 
nsEarth.onFinish(function(data){
    console.log(data); // Hello World
});

nsMars.onFinish(function(data){
    console.log(data); // Hello Mars
});

Next Steps und Release

Der PoC wird aus der Technik weiterentwickelt und in ein Modul verpackt, dass über die üblichen Kanäle wie bower und npm zu beziehen ist. Wir werden als nächstes den BackboneJS basierten Code verallgemeinern, da wir denken, dass auch andere Frameworks von LauchpadJS profitieren können Nach Aufräumarbeiten und Real-Life-Tests in unseren Projekten veröffentlichen wir den Code auf GitHub. https://github.com/aus-der-Technik

Es gibt noch ein riesiges Backlog an Ideen, die uns während der Entwicklung eingefallen sind, die das Leben mit dem TaskManager erleichtern. Um ein supportetes Modul zu deployen werden wir diese projektieren und entwickeln, sobald wir die Basisversion veröffentlich haben. Wir freuen uns über Anregungen.

Für Fragen stehen wir jederzeit gerne zur Verfügung. Wir freuen uns auf Besuch und Gespräch bei leckerem Kaffee in Offenbach am Meer. Komm vorbei!