When faced with either doing something nonsensical or aborting with an error, it will do something nonsensical. Anything is better than nothing.(Lexy Munroe über PHP, aber es passt hier hervorragend)
Look, it’s easy. Code everything in Typescript. All modules that use Fetch compile them to target ES6, transpile them with Babel on a stage-3 preset, and load them with SystemJS. If you don’t have Fetch, polyfill it, or use Bluebird, Request or Axios, and handle all your promises with await.(Jose Aguinaga im Jahr 2016, die Situation hat sich nicht merklich gebessert)
Design patterns are bug reports against your programming language
– Peter Norvig (Paraphrasiert und Erläutert, Vortrag)
...
)Function.apply
)const n1 = ["Julian", "Richard", "Anne", "George", "Timmy"];
const n2 = ["Tim", "Karl", "Willi", "Gabi"];
console.log(["Justus", "Peter", "Bob", ...n1, ...n2]);
const add = (lhs, rhs) => lhs + rhs;
const numbers = [1, 2];
console.log(add(...numbers));
console.log(add(...n1));
[ 'Justus',
'Peter',
'Bob',
'Julian',
'Richard',
'Anne',
'George',
'Timmy',
'Tim',
'Karl',
'Willi',
'Gabi' ]
3
JulianRichard
let a = 0, b = 1, r = 2;
[a, b] = [10, 20];
console.log("1) a =", a);
console.log("1) b =", b);
console.log("1) r =", r);
[a, b] = [b, a]
console.log("2) a =", a);
console.log("2) b =", b);
[a, b, ...r] = [30, 40, 50, 60, 70]
console.log("3) a =", a);
console.log("3) b =", b);
console.log("3) r =", r);
({a, b} = { a: 80, c: 90 }); // Round braces required!
console.log("4) a =", a);
console.log("4) b =", b);
1) a = 10
1) b = 20
1) r = 2
2) a = 20
2) b = 10
3) a = 30
3) b = 40
3) r = [ 50, 60, 70 ]
4) a = 80
4) b = undefined
obj => obj[name]
) greift auf einen Parameter der äußeren Funktion zu// Nutzung von Closure-Variable (name)
const extractField = function(name) {
return (obj => obj[name]);
}
const extractName = extractField("name");
console.log(extractName({ "name": "Alfred" }));
console.log(extractField("name")({ "name": "Alfred" }));
Alfred
Alfred
map
und filter
mit Closures// Nutzung von Closure-Variable (name)
const extractField = function(name) {
return (obj => obj[name]);
}
// Nutzung von Closure-Variable (minLen)
const minStrLen = function(minLen) {
return (str => str.length >= minLen);
}
const complex_data = [
{ name: "Phineas", hair: "red" },
{ name: "Ferb", hair: "green" },
{ name: "Agent P", hair: "green" },
{ name: "Candice", hair: "blonde" },
]
const hairLength = 5;
console.log("Haircolours >= " + hairLength + " chars: ",
complex_data
.map(extractField("hair"))
.filter(minStrLen(hairLength))
);
Haircolours >= 5 chars: [ 'green', 'green', 'blonde' ]
filter
, map
und reduce
mit Closuresconst deepCopy = function(obj) {
// Nicht-so-eleganter-Hack
return (JSON.parse(JSON.stringify(obj)));
}
const currentAge = function(person) {
let currentYear = new Date().getFullYear();
person.age = currentYear - person.born
return (person);
}
// Nutzung von Closure-Variable (fieldName)
const sumField = function(fieldName) {
return ((akku, obj) => akku + obj[fieldName]);
}
// 2x Nutzung von Closure-Variable (fieldName, len)
const minLength = function(fieldName, len) {
return (obj => obj[fieldName].length >= len)
}
const complex_data = [
{ name: "Matt", hair: "brown", born: 1978 },
{ name: "Chris", hair: "brown", born: 1978 },
{ name: "Dominic", hair: "blonde", born: 1977 },
]
console.log(
complex_data
.map(deepCopy)
.map(currentAge)
.filter(minLength("name", 5))
.filter(person => person.age > 39)
.reduce(sumField("age"), 0)
);
83
function
, aufruf “wie gewöhnlich” mit myVar()
this
-Kontext gesetzt werdenthis
-Kontext, der unterschiedlichste Werte annehmen kannthis
in anderen Sprachenthis
kann für jeden Aufruf aufs neue gesetzt werdenthis
fungiert somit als weiterer Parameter außerhalb der Parameterlistethis
-Kontext haben, lässt sich jede mit function
definierte Funktion auch als Methode verstehen.
Function.call
call(thisArg, arg1, arg2, ...)
this
-Kontext, dann folgen die “normalen” Parameterfunction
- und Arrow-Funktionenthis
-Kontextes zuconst greetFunction = function(job) {
console.log(`Hallo ${this.name}, du bist ${job}!`);
};
const greetArrow = (job) => {
console.log(`Hallo ${this.name}, du bist ${job}!`);
};
const person = {
"name" : "Justus"
};
greetFunction.call(person, "Detektiv");
greetArrow.call(person, "Detektiv");
Hallo Justus, du bist Detektiv!
Hallo undefined, du bist Detektiv!
Function.apply
apply(thisArg, [argArray])
this
-Kontext, die Parameter werden als ein Array übergebenconst greetFunction = function(job, car) {
console.log(`Hallo ${this.name}, du bist ${job} und fährst ${car}!`);
};
const greetArrow = (job, car) => {
console.log(`Hallo ${this.name}, du bist ${job} und fährst ${car}!`);
};
const person = {
"name" : "Peter"
};
greetFunction.apply(person, ["Detektiv", "MG"]);
greetArrow.apply(person, ["Detektiv", "MG"]);
Hallo Peter, du bist Detektiv und fährst MG!
Hallo undefined, du bist Detektiv und fährst MG!
Function.bind
bind(thisArg, arg1, arg2, ...)
call
const greetFunction = function(job, car) {
console.log(`Hallo ${this.name}, du bist ${job} und fährst ${car}!`);
};
const person = {
"name" : "Bob"
};
const partialGreet = greetFunction.bind(person, "Detektiv");
partialGreet();
partialGreet("VW Käfer");
Hallo Bob, du bist Detektiv und fährst undefined!
Hallo Bob, du bist Detektiv und fährst VW Käfer!
this
wird an das entsprechende Objekt gebundenfunction
für “Methoden im Sinne einer Klassen” nutzen, Arrow-Funktionen für Funktionenconst person = {
name : "Titus",
job : "Schrotthändler",
greetFunc : function() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
},
greetArrow : () => {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
}
}
person.greetFunc();
person.greetArrow();
Hallo Titus, du bist Schrotthändler!
Hallo undefined, du bist undefined!
this
-Kontext wird transitiv auch für Kind-Objekte gesetztthis
in Funktionen die Teil von person.address
sind zeigen auch auf person.address
const person = {
name : "Titus",
job : "Schrotthändler",
greet : function() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
},
address : {
city: "Rocky Beach",
print : function() {
console.log(`Adresse: ${this.city}`);
}
}
}
person.greet();
person.address.print();
Hallo Titus, du bist Schrotthändler!
Adresse: Rocky Beach
const titus = {
name : "Titus",
job : "Schrotthändler",
greet : function() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
}
};
const john = {
name : "John William Melvin Roger",
job : "Journalist",
greet : function() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
}
};
titus.greet();
john.greet();
Hallo Titus, du bist Schrotthändler!
Hallo John William Melvin Roger, du bist Journalist!
Welche Eigenschaft von Klassen fehlen den hier vorgestellten Objekten mit Funktionen?
Keine automatische Instanzierung gleichartiger Objekte möglich, die Methoden müssten für jedes konkrete Objekt wiederholt werden. Daran würde auch ein auslagern der greet
-Methode nichts ändern!
const makePerson = (name, job) => {
return ({
"name" : name,
"job" : job,
"greet" : function() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
}
});
}
const titus = makePerson('Titus', 'Schrotthändler');
const john = makePerson('John William Melvin Roger', 'Journalist');
titus.greet();
john.greet();
Hallo Titus, du bist Schrotthändler!
Hallo John William Melvin Roger, du bist Journalist!
Welche Eigenschaft von Klassen fehlen den hier vorgestellten Objekten mit Konstruktorfunktionen?
Vererbung
this
-Kontext, dem man Werte zuweisen kannconst Person = function(name, job) {
this.name = name;
this.job = job;
this.greet = function() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
};
// Dangerously wrong: Always points to the same function object
return (this);
};
const p1 = Person("Alfred", "Erzähler");
const p2 = Person("Skinny", "Ratte");
p1.greet();
p2.greet();
Hallo Skinny, du bist Ratte!
Hallo Skinny, du bist Ratte!
new
-Operatornew
-Operatorreturn
notwendig oder sinnvollconst Person = function(name, job) {
this.name = name;
this.job = job;
this.greet = function() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
};
};
const p1 = new Person("Alfred", "Erzähler");
const p2 = new Person("Skinny", "Ratte");
p1.greet();
p2.greet();
Hallo Alfred, du bist Erzähler!
Hallo Skinny, du bist Ratte!
prototype
-Eigenschaft, dessen Eigenschaften implizit auf dem Objekt verfügbar sindprototype
-Objekt über die Eigenschaft?undefined
const Person = function(name, job) {
this.name = name;
this.job = job;
};
Person.prototype.greet = function() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
};
const p1 = new Person("Alfred", "Erzähler");
const p2 = new Person("Skinny", "Ratte");
p1.greet();
p2.greet();
Hallo Alfred, du bist Erzähler!
Hallo Skinny, du bist Ratte!
static
)const Person = function(name, job) {
this.name = name;
this.job = job;
Person.count++;
};
Person.count = 0;
Person.prototype.greet = function() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
};
const p1 = new Person("Alfred", "Erzähler");
const p2 = new Person("Skinny", "Ratte");
p1.greet();
p2.greet();
console.log(`Es gibt ${Person.count} Personen`);
Hallo Alfred, du bist Erzähler!
Hallo Skinny, du bist Ratte!
Es gibt 2 Personen
prototype
-Ketteprototype
-Objektes stehen implizit jeder Instanz zur Verfügungprototype
-Objekte können ihrerseits wieder eine prototype
-Eigenschaft habencall
, erster Parameter ist this
const Person = function(name, job) {
this.name = name;
this.job = job;
};
Person.prototype.greet = function() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
};
const Detective = function(name, number) {
Person.call(this, name, "Detektiv");
this.number = number;
}
Detective.prototype = Object.create(Person.prototype);
Detective.prototype.greet = function() {
console.log(`Du bist ${this.name}, der ${this.number}. Detektiv`);
}
const p1 = new Person("Henry", "Filmtrickspezialist");
const p2 = new Detective("Peter", 2);
p1.greet();
p2.greet();
Hallo Henry, du bist Filmtrickspezialist!
Du bist Peter, der 2. Detektiv
class
-Syntaxprototype
Java
oder C#
, Funktionsumfang bleibt unverändertprototype
-Objekteclass Person {
constructor(name, job) {
this.name = name;
this.job = job;
}
greet() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
}
};
const p1 = new Person("Alfred", "Erzähler");
const p2 = new Person("Skinny", "Ratte");
p1.greet();
p2.greet();
Hallo Alfred, du bist Erzähler!
Hallo Skinny, du bist Ratte!
extends
/ Zugriff auf Basisklasse mit super
super
super
liefert die tatsächliche Basisklasse, nicht die aufrufende Methodegreet
der Oberklasse aufzurufen, wäre die Syntax also super.greet()
super()
möglichclass Person {
constructor(name, job) {
this.name = name;
this.job = job;
}
greet() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
}
};
class Detective extends Person {
constructor(name, number) {
super(name, "Detektiv");
this.number = number;
}
greet() {
console.log(`Du bist ${this.name}, der ${this.number}. Detektiv`);
}
};
const p1 = new Person("Henry", "Filmtrickspezialist");
const p2 = new Detective("Peter", 2);
p1.greet();
p2.greet();
Hallo Henry, du bist Filmtrickspezialist!
Du bist Peter, der 2. Detektiv
addEventListener
addEventListener
setzt this
auf das geklickte Elementclass ClickCounterArrow {
constructor(elem) {
this.elem = elem;
this.numClicks = 0;
this.elem.addEventListener('click', () => {
this.numClicks++;
this.elem.textContent = `Arrow: Clicked ${this.numClicks} times`;
console.log(this);
});
}
}
class ClickCounterFunction {
constructor(elem) {
this.elem = elem;
this.numClicks = 0;
const self = this;
this.elem.addEventListener('click', function() {
self.numClicks++;
self.elem.textContent = `Func: Clicked ${this.numClicks} times`;
console.log(this);
});
}
}
new ClickCounterArrow(document.querySelector('#arrow'));
new ClickCounterFunction(document.querySelector('#func'));
get
- und set
-Methoden ohne Syntax für FunktionsaufrufeObject.defineProperty
name
soll unmittelbar gelesen und geschrieben werden könnenbirthyear
soll alle Geburtsdaten minimal auf den Wert 1900 festlegenage
soll aus birthyear
das Alter in Jahren berechnen, aber keine Schreibvorgänge zulassenconst Person = function(name, birthyear) {
Object.defineProperty(this, 'name', {
value: name
});
Object.defineProperty(this, 'birthyear', {
get: function() {
return (this._birthyear);
},
set: function(value) {
this._birthyear = Math.max(+value, 1900);
},
});
this.birthyear = birthyear;
Object.defineProperty(this, 'age', {
get: function() {
return (new Date().getFullYear() - this._birthyear);
}
});
};
const people = [new Person("robert", 1909),
new Person("alfred", 1899)];
people.forEach(p => {
console.log("Not 12:", [p.name, p.birthyear, p.age].join(','));
p.age = 12;
console.log("12? :", [p.name, p.birthyear, p.age].join(','));
});
Not 12: robert,1909,110
12? : robert,1909,110
Not 12: alfred,1900,119
12? : alfred,1900,119
get
und set
class Person {
constructor(name, birthyear) {
this.name = name;
this.birthyear = birthyear;
}
get name() { return (this._name); }
set name(value) { this._name = value; }
get birthyear() { return (this._birthyear); }
set birthyear(value) {
this._birthyear = Math.max(+value, 1900);
}
get age() {
return (new Date().getFullYear() - this._birthyear);
}
};
const people = [new Person("robert", 1909),
new Person("alfred", 1899)];
people.forEach(p => {
console.log("Not 12:", [p.name, p.birthyear, p.age].join(','));
p.age = 12;
console.log("12? :", [p.name, p.birthyear, p.age].join(','));
});
Not 12: robert,1909,110
12? : robert,1909,110
Not 12: alfred,1900,119
12? : alfred,1900,119
string
, Array
, … verfügen ebenfalls über die prototype
-Schnittstellecore.js
String
-Methode endsWith
const hasEndsWith = !!String.prototype.endsWith;
if (!String.prototype.endsWith) {
String.prototype.endsWith = function(searchString, position) {
var subjectString = this.toString();
if (typeof position !== 'number'
|| !isFinite(position)
|| Math.floor(position) !== position
|| position > subjectString.length) {
position = subjectString.length;
}
position -= searchString.length;
var lastIndex = subjectString.lastIndexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
console.log("Patched String.endsWith");
} else {
console.log("No patching required");
}
<!-- Only here for the link -->
No patching required
console.log
console.log
mit einer HTML
-Ausgabe(function () {
const prev = console.log;
const logElem = document.querySelector('#log');
console.log = function () {
prev.apply(console, arguments);
Array.from(arguments).forEach(arg => {
if (typeof arg === "string") {
logElem.innerText += arg;
} else {
logElem.innerText += JSON.stringify(arg);
}
logElem.innerText += ' ';
});
logElem.innerText += '\n';
}
})();
console.log("Foo", 1, [1,2,3]);
console.log({ foo: "bar" });
<h1>Logausgaben</h1>
<pre id="log"></pre>
next
-Methodevalue
ist ein irgendwie geartetes “nächstes” Elementdone
kann auf true
gesetzt werden um das Ende des Iterators zu signalisierenclass CountIterator {
constructor() {
this._current = 0;
}
next() {
this._current += 1;
return ({
value : this._current,
done : false
});
}
}
console.log("CountIterator");
const count = new CountIterator();
for (let i = 0; i < 15; ++i) {
console.log(count.next().value);
}
class WrappingCountIterator {
constructor(length) {
this._length = length;
this._current = 0;
}
next() {
this._current = (this._current + 1) % this._length;
return ({
value : this._current,
done : false
});
}
}
console.log("WrappingCountIterator");
const countWrap = new WrappingCountIterator(3);
for (let i = 0; i < 15; ++i) {
console.log(countWrap.next().value);
}
CountIterator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
WrappingCountIterator
1
2
0
1
2
0
1
2
0
1
2
0
1
2
0
return
-artigen Schlüsselwortyield
an Stelle einer return
-Anweisung springt aus der Funktionyield
fortgesetztfunction*
als Schlüsselwort, danach Definition der Funktion wie bisheryield*
können in Generator-Funktionen weitere Generatorfunktionen aufgerufen werdenconst countGenerator = function*() {
for(let i = 0; true; ++i) {
yield i;
}
}
const count = countGenerator()
console.log(count.next().value);
console.log(count.next().value);
console.log(count.next().value);
const wrappingCountGenerator = function* (length) {
for(let i = 0; true; ++i) {
yield i % length;
}
}
const countWrap = wrappingCountGenerator(2);
console.log(countWrap.next().value);
console.log(countWrap.next().value);
console.log(countWrap.next().value);
console.log(countWrap.next().value);
0
1
2
0
1
0
1
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
for (let n of fibonacci()) {
console.log(n);
if (n >= 10) {
break;
}
}
1
2
3
5
8
13
for .. in ..
foreach
-Iteration über Wertefor .. in ..
bezieht Funktionen und die Vererbunghierarchie mit einObject.prototype.hasOwnProperty
kann zwischen eigenen und ererbten Eigenschaften unterscheidenJSON
-Objekte mit for .. in
eigentlich immer genutzt werdenconst Person = function(name, job) {
this.name = name;
this.job = job;
};
Person.prototype.greet = function() {
console.log(`Hallo ${this.name}, du bist ${this.job}!`);
};
const Detective = function(name, number) {
Person.call(this, name, "Detektiv");
this.number = number;
}
Detective.prototype = Object.create(Person.prototype);
Detective.prototype.greet = function() {
console.log(`Du bist ${this.name}, der ${this.number}. Detektiv`);
}
const p1 = new Detective("Peter", 2);
for (let property in p1) {
console.log(property);
}
name
job
number
greet
for .. in ..
einfach garnicht, sondern verwenden Sie die Funktion Object.keys()
.
for .. of ..
Array
, Map
, Set
, String
, Generatorfunktionen, arguments
, NodeList
aus dem DOM
, …Object
, allerdings kann über Object.entries()
iteriert werden, was wiederrum eine Funktion aus ECMA Script 2017
ist, die noch nicht in allen Browsern verfügbar ist.function* foo(){
yield 1;
yield 2;
yield 3;
};
for (const val of foo()) {
console.log(val);
}
for (const val of [1,2,3]) {
console.log(val);
}
for (const val of "123") {
console.log(val);
}
for (const val of {1: 'a', 2: 'b', 3: 'c'}) {
console.log(val);
}
1
2
3
1
2
3
1
2
3
[stdin]:19
for (const val of {1: 'a', 2: 'b', 3: 'c'}) {
^
TypeError: {(intermediate value)(intermediate value)(intermediate value)} is not iterable
at [stdin]:19:19
yield
mit yield*
function* iterateDomTree(curr, depth) {
if (depth === undefined) {
depth = 0;
}
yield ({
"elem" : curr,
"depth" : depth
});
for (const child of curr.children) {
yield* iterateDomTree(child, depth + 1);
}
}
for (const node of iterateDomTree(document.body)) {
console.log(node);
}
<ol>
<li>Justus</li>
<li>Peter</li>
<li>Bob</li>
</ol>
<p>
<a href="https://www.xing.com/communities/groups/ce8b-1068750">
FanGruppe auf XING
</a>
</p>
ES6
: Promise
Promise
-ObjektPromise
-Objektesfulfilled
, in diesem wird onFulfilled
“so früh wie möglich” aufgerufenrejected
, in diesem wird onRejected
“so früh wie möglich” aufgerufenpending
, wenn weder fulfilled
noch rejected
giltAJAX
)KI
-AbstraktionKI
oder menschlich) über Promise
abstrahierenUI
-Entwickler: Tolle Methode um Wartezeit-Dialoge zu kapselnPromise
-APIthen
-Methoden eines Promise
-Objektes: Hinterlegen von beliebig vielen Callbacks zur Verarbeitung der berechneten Ergebnissethen
mit unterschiedlichen Funktionenthen
-FunktiononFulfilled
) wird im Erfolgsfall aufgerufenonRejected
) ist optional und wird im Fehlerfall aufgerufenPromise
-Konstruktor: Übergabe einer Berechnungsfunktionresolve
und reject
als Parameter um das Ende der Berechnung signalisieren zu könnenerr
-Parameter überflüssigPromise
für AJAX
-Anfragenconst doAjaxRequest = url => {
return (new Promise((resolve, reject) => {
var request = new XMLHttpRequest();
request.open('GET', url);
request.onload = () => {
if (request.status === 200) {
resolve(request.responseText);
} else {
reject(Error(`Code ${request.status}: ${request.statusText}`));
}
};
request.onerror = () => {
reject(Error('Network Error'));
};
request.send();
}));
}
const promise = doAjaxRequest('/interactive/api/random/pokemon');
promise.then(
(res) => document.body.innerHTML = res,
(err) => console.log(err)
);
Promise.resolve
onFulfilled
-Callbacks ist kein Promise
HTTP
-Endpunkt liefert zufällige Ergebnisse (unabhängig vom Schlüssel)AJAX
-Anfrage) darf also nur exakt einmalig vorgenommen werdenthis.cache[key]
ist mal ein Promise
und mal nichtPromise
String
Promise
-Objektclass Cache {
constructor() {
this.cache = {};
}
getValue(key) {
if (!this.cache[key]) {
this.cache[key] = doAjaxRequest(`/interactive/api/cache/${key}`);
this.cache[key].then(result => this.cache[key] = result);
}
return (this.cache[key]);
}
}
const c = new Cache();
Promise.resolve(c.getValue(1)).then(v => console.log(v));
Promise.resolve(c.getValue(1)).then(v => console.log(v));
Promise.all(iterable)
: Liefert eine neue Promise
-Instanz die erfüllt wird, wenn alle Versprechen des Iterationsobjektes erfüllt sindPromise.race(iterable)
: Liefert eine neue Promise
-Instanz die erfüllt wird, wenn das erste Versprechen des Iterationsobjektes erfüllt istconst grabRandomValues = num => {
const toReturn = [];
for (let i = 0; i < num; ++i) {
const req = doAjaxRequest(`/interactive/api/cache/${i + 1}`);
req.then(v => console.log(`Anfrage ${i} fertig`));
toReturn.push(req);
}
return (toReturn);
};
const many = grabRandomValues(10);
Promise.all(many).then(v => console.log("Alles fertig"));
Promise.race(many).then(v => console.log("Erster fertig"));
async
/ await
Promise
async
: Markiert eine Funktion als asynchronawait
-Schlüsselwortesawait
: Wartet auf das Ergebnis einer Promise
-Berechnungasync
-Funktionthen
-Ergebnis ist Ergebnis des Ausdrucksreject
-Ergebnis wird als Ausnahme geworfensetTimeout
ruft die übergebene Funktion nach einer Zeitspanne in ms
aufsleep
console.log("program: enter");
function timeout(ms) {
console.log("timeout: enter");
const toReturn = new Promise(resolve => {
setTimeout(_ => {
console.log("timeout: resolve");
resolve(ms)
}, ms)
});
console.log("timeout: leave");
return (toReturn);
}
async function asyncCall() {
console.log("asyncCall: enter");
const result = await timeout(1000);
console.log("asyncCall: leave");
}
asyncCall();
console.log("program: leave");
program: enter
asyncCall: enter
timeout: enter
timeout: leave
program: leave
timeout: resolve
asyncCall: leave
AJAX
-Beispiel mit async
/ await
const doAjaxRequest = url => {
return (new Promise((resolve, reject) => {
var request = new XMLHttpRequest();
request.open('GET', url);
request.onload = () => {
if (request.status === 200) {
resolve(request.responseText);
} else {
reject(Error(`Code ${request.status}: ${request.statusText}`));
}
};
request.onerror = () => {
reject(Error('Network Error'));
};
request.send();
}));
}
async function getRandomPokemon() {
return await doAjaxRequest('/interactive/api/random/pokemon');
}
document.body.innerHTML = getRandomPokemon();
async
/ await
-Folgefunction timeout(ms) {
return new Promise(resolve => setTimeout(_ => resolve(ms), ms))
}
async function longProcess() {
await timeout(2000);
document.body.innerHTML = await doAjaxRequest('/interactive/api/random/pokemon');
await timeout(2000);
document.body.innerHTML = await doAjaxRequest('/interactive/api/random/harry-potter');
await timeout(2000);
}
longProcess();
Promise
: Berechnet nicht mehr als einen Wertmap
oder filter
Observable
Observable.subscribe
Promise.then
aufgefasst werdenonNext
und wird möglicherweise mehr als einmal aufgerufenvar button = document.querySelector('#subscribe');
Rx.Observable.fromEvent(button, 'click')
.subscribe(() => console.log('#subscribe clicked!'));
map
const textarea = document.querySelector('#subscribe');
const target = document.querySelector('#target');
Rx.Observable.fromEvent(textarea, 'keyup')
.map(event => event.target.value)
.subscribe(v => target.innerHTML = v);
Observable.range(start, length)
onCompleted
, signalisiert das Ende des Datenstromsconst source = Rx.Observable.range(1, 10);
source.subscribe(
x => console.log(`Value: ${x}`),
err => console.log(`Error: ${err}`),
() => console.log("Finished")
);
Observable.interval(ms)
ms
Millisekundentake
begrenzt die Anzahl der Werte, die der Quelle entnommen werdenfilter
lässt keine Werte durch, die nicht dem Filterkriterium entsprechenconst source = Rx.Observable.interval(500);
source
.filter(x => x % 2 == 0)
.take(5)
.subscribe(
x => console.log(`Even value: ${x}`),
err => console.log(`Even error: ${err}`),
() => console.log("Even finished")
);
source
.take(10)
.subscribe(
x => console.log(`Value: ${x}`),
err => console.log(`Error: ${err}`),
() => console.log("Finished")
);
debounce
gibt Elemente erst weiter, wenn für eine bestimmte Zeitspanne keine neuen Elemente erzeugt wurdendistinctUntilChanged
gibt nur vom jeweils vorherigen Elemente abweichende Elemente weiterconst textarea = document.querySelector('#subscribe');
const target = document.querySelector('#target');
Rx.Observable.fromEvent(textarea, 'keyup')
.map(event => event.target.value)
.debounceTime(500)
.distinctUntilChanged()
.subscribe(v => target.innerHTML = v);
import
- oder require
-Anweisungen referenziert werdenpipe()
-Aufruf eingewickelt werdenHTTP/1
gilt: Jeder Zugriff auf eine neue Ressource ist vergleichsweise teuerTCP
-Verbindung muss ggfs. auf und abgebaut werdenHTTP/2
, lesenswerter Artikel von Ashley Rich: Performance Best Practices in the HTTP/2 EraHTTP/1
und HTTP/2
// Working with objects
const homes = {
"phineas": "Maple Drive 2308",
"ferb": "Maple Drive 2308",
"doofenschmirtz": "Doofenshmirtz Evil Inc."
}
homes.isabella = "across from the Flynn-Fletcher house";
homes["Major Monogram"] = "O.W.C.A. Secret Headquarters";
const keys = Object.keys(homes);
for (let index = 0; index < keys.length; ++index) {
const key = keys[index];
console.log(key, "=>", homes[key]);
}
// Working with arrays
const names = ["Phineas", "Ferb"];
names[3] = "Candice";
for (let index = 0; index < names.length; ++index) {
console.log("Name #" + index + ": " + names[index]);
}
HTTP/1
relevant! Mit HTTP/2
lassen sich viele kleine Dateien effizient übertragen.
CSS
-Dateien analog zu JavaScript-CodeCSS