Skip to content

Decorators and forwarding, call/apply #204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
// usiamo ...args invece di arguments per memorizzare un vero array in 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
// il tuo codice
}


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.
Il wrapper restituito da `spy (f)` dovrebbe memorizzare tutti gli argomenti e quindi usare `f.apply` per inoltrare la chiamata.
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ importance: 5

---

# Spy decorator
# decorator spia

Create a decorator `spy(func)` that should return a wrapper that saves all calls to function in its `calls` property.
Crea un decorator `spy(func)` che restituisca un wrapper che salva tutte le chiamate alla funzione nella sua proprietà `calls`.

Every call is saved as an array of arguments.
Ogni chiamata viene salvata come un array di argomenti.

For instance:
Ad esempio:

```js
function work(a, b) {
alert( a + b ); // work is an arbitrary function or method
alert( a + b ); // work è una funzione o un metodo arbitrario
}

*!*
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. Questo decorator a volte è utile per fare *unit-testing*. La sua forma avanzata è `sinon.spy` nella libreria [Sinon.JS](http://sinonjs.org/).
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("chiama la funzione dopo il timeout specificato", function () {
let start = Date.now();

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

it("passes arguments and this", function() {
it("passa argomenti e 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(who, "John");
Expand All @@ -41,6 +41,6 @@ describe("delay", function() {

this.clock.tick(2000);

assert(spy.calledOnce, 'calledOnce check failed');
assert(spy.calledOnce, 'controllo di calledOnce fallito');
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The solution:
La soluzione:

```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"); // mostra "test" dopo 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.
Qui, nota come viene utilizzata un arrow function. come sappiamo le arrow functions non hanno un proprio `this` `arguments`, quindi `f.apply(this, arguments)` prende `this` e `arguments` dal wrapper.

If we pass a regular function, `setTimeout` would call it without arguments and `this=window` (assuming we're in the browser).
Se passassimo una funzione regolare, `setTimeout` la chiamerebbe senza argomenti e` this = window` (supponendo essere in un browser).

We still can pass the right `this` by using an intermediate variable, but that's a little bit more cumbersome:
Possiamo anche passare il `this` corretto usando una variabile intermedia, ma è un po' più complicato:

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

return function(...args) {
let savedThis = this; // store this into an intermediate variable
let savedThis = this; // memorizzalo in una variabile intermedia
setTimeout(function() {
f.apply(savedThis, args); // use it here
f.apply(savedThis, args); // usalo qui
}, ms);
};

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

---

# Delaying decorator
# decorator ritardante

Create a decorator `delay(f, ms)` that delays each call of `f` by `ms` milliseconds.
Crea il decorator `delay(f, ms)` che ritarda ogni chiamata ad `f` di `ms` millisecondi.

For instance:
Ad esempio:

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

// create wrappers
// crea i wrappers
let f1000 = delay(f, 1000);
let f1500 = delay(f, 1500);

f1000("test"); // shows "test" after 1000ms
f1500("test"); // shows "test" after 1500ms
f1000("test"); // visualizza "test" dopo 1000ms
f1500("test"); // visualizza "test" dopo 1500ms
```

In other words, `delay(f, ms)` returns a "delayed by `ms`" variant of `f`.
In altre parole, `delay(f, ms)` ritorna una variante di `f` ritardata di `ms`.

In the code above, `f` is a function of a single argument, but your solution should pass all arguments and the context `this`.
Nel codice sopra, `f` è una funzione con un solo argomento, ma la tua soluzione potrebbe passare molti argomenti ed il contesto `this`.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ describe('debounce', function () {
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); // ignorato (tropo presto)
setTimeout(() => debounced('c'), 500); // eseguito (1000 ms passati)
this.clock.tick(1000);

assert(f.notCalled, 'not called after 1000ms');
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,24 @@
<!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:
Function <code>handler</code> viene chiamata su questo input:
<br>
<input id="input1" placeholder="type here">

<p>

Debounced function <code>debounce(handler, 1000)</code> is called on this input:
<br>
<input id="input2" placeholder="type here">
Debounced function <code>debounce(handler, 1000)</code> viene chiamata su questo input:
<br>
<input id="input2" placeholder="type here">

<p>
<button id="result">The <code>handler</code> puts the result here</button>
<button id="result">L' <code>handler</code> mette il risultato qui</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>
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.
Una chiamata a `debounce` restituisce un wrapper. Quando viene chiamato, pianifica la chiamata della funzione originale dopo tot `ms` e annulla il precedente timeout.

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.
Il risultato del decorator `debounce(f, ms)` è un wrapper che sospende le chiamate a `f` finché non ci sono `ms` millisecondi di inattività (nessuna chiamata, "periodo di *cooldown*"), quindi invoca `f` una volta, con gli ultimi argomenti.

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`).
In altre parole, `debounce` è come una segretaria che riceve "telefonate" e aspetta finché non ci sono `ms` di silenzio. Solo allora trasferisce le informazioni sull'ultima chiamata al "capo" (chiama effettivamente `f`).

For instance, we had a function `f` and replaced it with `f = debounce(f, 1000)`.
Ad esempio, avevamo una funzione `f` e l'abbiamo sostituita con `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.
Se la funzione incapsulata viene chiamata a 0 ms, 200 ms e 500 ms, e quindi non ci sono chiamate, la `f` effettiva verrà chiamata solo una volta, a 1500 ms. Cioè dopo il periodo di *cooldown* di 1000 ms dall'ultima chiamata.

![](debounce.svg)

...And it will get the arguments of the very last call, other calls are ignored.
... E riceverà gli argomenti dell'ultima chiamata, tutte le altre chiamate vengono ignorate.

Here's the code for it (uses the debounce decorator from the [Lodash library](https://lodash.com/docs/4.17.15#debounce)):
Ecco il codice, (usa il *debounce decorator* dalla [Libreria Lodash](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")
// la funzione di debounced attende 1000ms dopo l'ultima chiamata, quini esegue: 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.
Ora un esempio pratico. Diciamo che l'utente digita qualcosa, e vorremmo inviare una richiesta al server quando l'input è finito.

There's no point in sending the request for every character typed. Instead we'd like to wait, and then process the whole result.
Non ha senso inviare la richiesta per ogni carattere digitato. Vorremmo invece aspettare, e poi elaborare l'intero risultato.

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.
In un browser web, possiamo impostare un gestore di eventi -- una funzione che viene chiamata ad ogni modifica di un campo di input. Normalmente, un gestore di eventi viene chiamato molto spesso, per ogni tasto digitato. Ma se utilizziamo un `debounce` di 1000 ms, verrà chiamato solo una volta, dopo 1000 ms dopo l'ultimo input.

```online

In this live example, the handler puts the result into a box below, try it:
In questo esempio dal vivo, il gestore inserisce il risultato in una box sottostante, provalo:

[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.
Come vedi, il secondo input chiama la funzione, quindi il suo contenuto viene elaborato dopo 1000 ms dall'ultimo inserimento.
```

So, `debounce` is a great way to process a sequence of events: be it a sequence of key presses, mouse movements or something else.
Quindi, `debounce` è un ottimo modo per elaborare una sequenza di eventi: che si tratti di una sequenza di pressioni di tasti, movimenti del mouse o qualsiasi altra cosa.

It waits the given time after the last call, and then runs its function, that can process the result.
Aspetta il tempo specificato dopo l'ultima chiamata, quindi esegue la sua funzione, che può elaborare il risultato.

The task is to implement `debounce` decorator.
Il compito è implementare il decorator `debounce`.

Hint: that's just a few lines if you think about it :)
Suggerimento: sono solo poche righe se ci pensi :)
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
// memorizza gli ultimi argomenti per la chiamata dopo il cooldown
savedArgs = arguments;
savedThis = this;
return;
}

// otherwise go to cooldown state
// altrimenti imposta stato di cooldown
func.apply(this, arguments);

isThrottled = true;

// plan to reset isThrottled after the delay
setTimeout(function() {
// pianifica il reset isThrottled dopo il ritardo
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
// se ci sono state chiamate, savedThis/savedArgs ha l'ultima
// la chiamata ricorsiva esegue la funzione e reimposta il cooldown
wrapper.apply(savedThis, savedArgs);
savedArgs = savedThis = null;
}
Expand Down
Loading