Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function spy(func) {

function wrapper(...args) {
// using ...args instead of arguments to store "real" array in wrapper.calls
// bruger ...args i stedet for arguments for at gemme et "rigtigt" array i wrapper.calls
wrapper.calls.push(args);
return func.apply(this, args);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function spy(func) {
// your code
// din kode
}


Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe("spy", function() {
it("records calls into its property", function() {
function work() {}
describe("spy", function () {
it("optager kald og gemmer dem i dens egenskab calls", function () {
function work() { }

work = spy(work);
assert.deepEqual(work.calls, []);
Expand All @@ -17,7 +17,7 @@ describe("spy", function() {
]);
});

it("transparently wraps functions", function() {
it("transperant wrap af funktioner", function () {

let sum = sinon.spy((a, b) => a + b);

Expand All @@ -28,7 +28,7 @@ describe("spy", function() {
});


it("transparently wraps methods", function() {
it("transparent wrap af metoder", function () {

let calc = {
sum: sinon.spy((a, b) => a + b)
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
The wrapper returned by `spy(f)` should store all arguments and then use `f.apply` to forward the call.
Wrapperen der returneres af `spy(f)` skal gemme alle argumenter og derefter bruge `f.apply` til at videregive kaldet.
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ importance: 5

# Spy decorator

Create a decorator `spy(func)` that should return a wrapper that saves all calls to function in its `calls` property.
Opret en decorator `spy(func)` der returnerer en wrapper der gemmer alle kald til funktionen i dens `calls` egenskab.

Every call is saved as an array of arguments.
Hvert kald er gemt som et array af argumenter.

For instance:
For eksempel:

```js
function work(a, b) {
alert( a + b ); // work is an arbitrary function or method
alert( a + b ); // work er en tilfældig funktion eller metode
}

*!*
Expand All @@ -27,4 +27,4 @@ for (let args of work.calls) {
}
```

P.S. That decorator is sometimes useful for unit-testing. Its advanced form is `sinon.spy` in [Sinon.JS](http://sinonjs.org/) library.
P.S. Denne decorator kan være brugbar ved unit-testing. En avanceret form er `sinon.spy` i [Sinon.JS](http://sinonjs.org/) biblioteket.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
describe("delay", function() {
before(function() {
describe("delay", function () {
before(function () {
this.clock = sinon.useFakeTimers();
});

after(function() {
after(function () {
this.clock.restore();
});

it("calls the function after the specified timeout", function() {
it("kalder funktionen efter den givne timeout", function () {
let start = Date.now();

function f(x) {
Expand All @@ -18,15 +18,15 @@ describe("delay", function() {
let f1000 = delay(f, 1000);
f1000("test");
this.clock.tick(2000);
assert(f.calledOnce, 'calledOnce check fails');
assert(f.calledOnce, 'calledOnce check fejlede');
});

it("passes arguments and this", function() {
it("videregiver argumenter og this", function () {
let start = Date.now();
let user = {
sayHi: function(phrase, who) {
sayHi: function (phrase, who) {
assert.equal(this, user);
assert.equal(phrase, "Hello");
assert.equal(phrase, "Hej");
assert.equal(who, "John");
assert.equal(Date.now() - start, 1500);
}
Expand All @@ -37,10 +37,10 @@ describe("delay", function() {
let spy = user.sayHi;
user.sayHi = delay(user.sayHi, 1500);

user.sayHi("Hello", "John");
user.sayHi("Hej", "John");

this.clock.tick(2000);

assert(spy.calledOnce, 'calledOnce check failed');
assert(spy.calledOnce, 'calledOnce check fejlede');
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The solution:
Løsningen:

```js run demo
function delay(f, ms) {
Expand All @@ -11,22 +11,22 @@ function delay(f, ms) {

let f1000 = delay(alert, 1000);

f1000("test"); // shows "test" after 1000ms
f1000("test"); // viser "test" efter 1000ms
```

Please note how an arrow function is used here. As we know, arrow functions do not have own `this` and `arguments`, so `f.apply(this, arguments)` takes `this` and `arguments` from the wrapper.
Bemærk hvordan en arrow function er brugt her. Som vi ved, har arrow functions ingen egen `this` og `arguments`, `f.apply(this, arguments)` tager `this` og `arguments` fra wrapperen.

If we pass a regular function, `setTimeout` would call it without arguments and `this=window` (assuming we're in the browser).
Hvis vi sender en almindelig funktion, vil `setTimeout` kalde den uden argumenter og med `this=window` (hvis vi er i browseren).

We still can pass the right `this` by using an intermediate variable, but that's a little bit more cumbersome:
Vi kan stadig videregive det rigtige `this` ved at bruge en midlertidig variabel, men det er lidt mere besværligt:

```js
function delay(f, ms) {

return function(...args) {
let savedThis = this; // store this into an intermediate variable
let savedThis = this; // gem this som en midlertidig variabel
setTimeout(function() {
f.apply(savedThis, args); // use it here
f.apply(savedThis, args); // brug den her
}, ms);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@ importance: 5

---

# Delaying decorator
# Forsinkelses decorator

Create a decorator `delay(f, ms)` that delays each call of `f` by `ms` milliseconds.
Opret en decorator `delay(f, ms)` der forsinker hvert kald af `f` med `ms` millisekunder.

For instance:
For eksempel:

```js
function f(x) {
alert(x);
}

// create wrappers
// Opret wrappers der forsinker kaldet af f med 1000ms og 1500ms
let f1000 = delay(f, 1000);
let f1500 = delay(f, 1500);

f1000("test"); // shows "test" after 1000ms
f1500("test"); // shows "test" after 1500ms
f1000("test"); // viser "test" efter 1000ms
f1500("test"); // viser "test" efter 1500ms
```

In other words, `delay(f, ms)` returns a "delayed by `ms`" variant of `f`.
Med andre ord: `delay(f, ms)` returner en udgave af `f` der er "forsinket med `ms` millisekunder".

In the code above, `f` is a function of a single argument, but your solution should pass all arguments and the context `this`.
I koden ovenfor er `f` en funktion med et enkelt argument, men din løsning skal videregive alle argumenter og konteksten `this`.
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,33 @@ describe('debounce', function () {
this.clock.restore();
});

it('for one call - runs it after given ms', function () {
it('for et kald - kører den én gang efter givent antal millisekunder', function () {
const f = sinon.spy();
const debounced = debounce(f, 1000);

debounced('test');
assert(f.notCalled, 'not called immediately');
assert(f.notCalled, 'ikke kaldt med det samme');
this.clock.tick(1000);
assert(f.calledOnceWith('test'), 'called after 1000ms');
assert(f.calledOnceWith('test'), 'kaldt efter 1000ms');
});

it('for 3 calls - runs the last one after given ms', function () {
it('for 3 kald - kører den sidste efter givent antal millisekunder', function () {
const f = sinon.spy();
const debounced = debounce(f, 1000);

debounced('a');
setTimeout(() => debounced('b'), 200); // ignored (too early)
setTimeout(() => debounced('c'), 500); // runs (1000 ms passed)
setTimeout(() => debounced('b'), 200); // ignoreret (for tidligt)
setTimeout(() => debounced('c'), 500); // kører (1000 ms passeret)
this.clock.tick(1000);

assert(f.notCalled, 'not called after 1000ms');
assert(f.notCalled, 'ikke kaldt efter 1000ms');

this.clock.tick(500);

assert(f.calledOnceWith('c'), 'called after 1500ms');
assert(f.calledOnceWith('c'), 'kaldt efter 1500ms');
});

it('keeps the context of the call', function () {
it('beholder konteksten af kaldet', function () {
let obj = {
f() {
assert.equal(this, obj);
Expand All @@ -44,5 +44,5 @@ describe('debounce', function () {
obj.f('test');
this.clock.tick(5000);
});

});
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
<!doctype html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Function <code>handler</code> is called on this input:
<br>
<input id="input1" placeholder="type here">
Funktion
<code>handler</code>
kaldes på dette input:
<br />
<input id="input1" placeholder="skriv her" />

<p>

Debounced function <code>debounce(handler, 1000)</code> is called on this input:
<br>
<input id="input2" placeholder="type here">
Debounced funktion
<code>debounce(handler, 1000)</code>
kaldes på dette input:
<br />
<input id="input2" placeholder="skriv her" />
</p>

<p>
<button id="result">The <code>handler</code> puts the result here</button>
<button id="result">
<code>Handleren</code>
putter resultatet her
</button>

<script>
function handler(event) {
result.innerHTML = event.target.value;
}
<script>
function handler(event) {
result.innerHTML = event.target.value;
}

input1.oninput = handler;
input2.oninput = _.debounce(handler, 1000);
</script>
input1.oninput = handler;
input2.oninput = _.debounce(handler, 1000);
</script>
</p>
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ function debounce(func, ms) {

```

A call to `debounce` returns a wrapper. When called, it schedules the original function call after given `ms` and cancels the previous such timeout.
Et kald til `debounce` returnerer en wrapper. Når den kaldes, planlægger den et kald til den originale funktion efter `ms` millisekunder og annullerer tidligere timeouts hvis de findes.

Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,48 @@ importance: 5

# Debounce decorator

The result of `debounce(f, ms)` decorator is a wrapper that suspends calls to `f` until there's `ms` milliseconds of inactivity (no calls, "cooldown period"), then invokes `f` once with the latest arguments.
Resultatet af en `debounce(f, ms)` decorator er en wrapper der suspenderer kaldet til `f` indtil der er `ms` millisekunder af inaktivitet (ingen kald, "cooldown period"), så kalder den `f` én gang med de seneste argumenter.

In other words, `debounce` is like a secretary that accepts "phone calls", and waits until there's `ms` milliseconds of being quiet. And only then it transfers the latest call information to "the boss" (calls the actual `f`).
Med andre ord er `debounce` som en sekretær der tager imod "telefonopkald" og venter ind til der har været `ms` millisekunder af inaktivitet. Og først da overfører den de seneste opkaldsoplysninger til "chefen" (kalder den faktiske `f`).

For instance, we had a function `f` and replaced it with `f = debounce(f, 1000)`.
For eksempel, vi havde en funktion `f` og erstattede den med `f = debounce(f, 1000)`.

Then if the wrapped function is called at 0ms, 200ms and 500ms, and then there are no calls, then the actual `f` will be only called once, at 1500ms. That is: after the cooldown period of 1000ms from the last call.
Hvis den omgivende funktion kaldes ved 0ms, 200ms og 500ms, og der ikke er flere kald efter det, vil den faktiske `f` kun blive kaldt én gang, ved 1500ms. Det vil sige: efter cooldown-perioden på 1000ms fra det sidste kald.

![](debounce.svg)

...And it will get the arguments of the very last call, other calls are ignored.
...og vi vil få argumenterne fra det sidste kald, andre kald ignoreres.

Here's the code for it (uses the debounce decorator from the [Lodash library](https://lodash.com/docs/4.17.15#debounce)):
Her er koden for det (som bruger debounce decorator fra [Lodash biblioteket](https://lodash.com/docs/4.17.15#debounce)):

```js
let f = _.debounce(alert, 1000);

f("a");
setTimeout( () => f("b"), 200);
setTimeout( () => f("c"), 500);
// debounced function waits 1000ms after the last call and then runs: alert("c")
// debounced funktion venter 1000ms efter det sidste kald og kalder så alert("c")
```

Now a practical example. Let's say, the user types something, and we'd like to send a request to the server when the input is finished.
Nu til et praktisk eksempel. Lad os sige, at brugeren skriver noget, og vi vil sende en forespørgsel til serveren når inputtet er færdigt.

There's no point in sending the request for every character typed. Instead we'd like to wait, and then process the whole result.
Der er ingen pointe i at sende forespørgslen for hver tastetryk. I stedet vil vi vente og så behandle hele resultatet.

In a web-browser, we can setup an event handler -- a function that's called on every change of an input field. Normally, an event handler is called very often, for every typed key. But if we `debounce` it by 1000ms, then it will be only called once, after 1000ms after the last input.
en webbrowser kan vi opsætte en event handler -- en funktion der kaldes ved hver ændring af et inputfelt. Normalt kaldes en event handler meget ofte, for hver tastetryk. Men hvis vi `debounce` den med 1000ms, vil den kun blive kaldt én gang, efter 1000ms efter det sidste input.

```online

In this live example, the handler puts the result into a box below, try it:
I dette live eksempel sætter handleren resultatet i en boks nedenfor, prøv det:

[iframe border=1 src="debounce" height=200]

See? The second input calls the debounced function, so its content is processed after 1000ms from the last input.
Kan du se effekten? Det andet input kalder den debounced funktion, så dens indhold behandles efter 1000ms fra det sidste input.
```

So, `debounce` is a great way to process a sequence of events: be it a sequence of key presses, mouse movements or something else.
`debounce` er en god måde at processere en række af events: en sekvens at tastetryk, musbevægelser eller andet.

It waits the given time after the last call, and then runs its function, that can process the result.
Den venter en given tid efter det sidste kald, og så kører den sin funktion, som kan behandle resultatet.

The task is to implement `debounce` decorator.
Din opgave er at implementere en `debounce` decorator.

Hint: that's just a few lines if you think about it :)
Hint: det er egentlig kun et par linjer hvis man tænker over det :)
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ function throttle(func, ms) {
function wrapper() {

if (isThrottled) {
// memo last arguments to call after the cooldown
// memoriser sidste sæt argumenter der skal kaldes efter cooldown
savedArgs = arguments;
savedThis = this;
return;
}

// otherwise go to cooldown state
// ellers gå til cooldown state
func.apply(this, arguments);

isThrottled = true;

// plan to reset isThrottled after the delay
// planlæg at nulstille isThrottled efter forsinkelsen
setTimeout(function() {
isThrottled = false;
if (savedArgs) {
// if there were calls, savedThis/savedArgs have the last one
// recursive call runs the function and sets cooldown again
// hvis der har været yderligere kald har savedThis/savedArgs de sidste
// rekursivt kald kører funktionen og sætter cooldown igen
wrapper.apply(savedThis, savedArgs);
savedArgs = savedThis = null;
}
Expand Down
Loading