Skip to content
Merged
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
// χρησιμοποιώντας ...args αντί για ορίσματα για να κρατήσουμε τον "πραγματικό" πίνακα στην wrapper.calls
wrapper.calls.push(args);
return func.apply(this, args);
}
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.
Η συνάρτηση-κάλλυμα που επιστρέφεται από τη `spy(f)` πρέπει να αποθηκεύει όλα τα ορίσματα και μετά να χρησιμοποιεί την `f.apply` για να προωθήσει τη κλήση.
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ importance: 5

---

# Spy decorator
# Διακοσμητής κατάσκοπος

Create a decorator `spy(func)` that should return a wrapper that saves all calls to function in its `calls` property.
Δημιούργησε ένα διακοσμητή `spy(func)` που επιστρέφει έναν wrapper που αποθηκέυει όλες τις κλήσεις στη συνάρτηση στην `calls` ιδιότητα του.

Every call is saved as an array of arguments.
Κάθε κλήση αποθηκέυεται σαν ένας πίνακας από ορίσματα.

For instance:
Για παράδειγμα:

```js
function work(a, b) {
alert( a + b ); // work is an arbitrary function or method
alert( a + b ); // η work είναι μια τυχαία συνάρτηση η μέθοδος
}

*!*
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.
Αυτός ο διακοσμητής είναι μερικές φορές χρήσιμος για unit-testing. Η προηγμένη του μορφή είναι το `sinon.spy` στην [Sinon.JS](http://sinonjs.org/) βιβλιοθήκη.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The solution:
Η λύση:

```js run demo
function delay(f, ms) {
Expand All @@ -13,20 +13,19 @@ let f1000 = delay(alert, 1000);

f1000("test"); // shows "test" after 1000ms
```
Παρατήρησε πώς μια συνάρτηση-arrow χρησιμοποιείται εδώ. Όπως γνωρίζουμε, οι συναρτήσεις arrow δεν έχουν δικό τους `this`και `arguments`, οπότε το `f.apply(this, arguments)`παίρνει το `this` και τα `arguments` από τη συνάρτηση-κάλυμμα.

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.
Αν περάσουμε μια απλή συνάρτηση, η `setTimeout` θα το καλέσει χωρίς ορίσματα και θα ισχύει `this=window` (εφόσον είμαστε στον δρομολογήτη).

If we pass a regular function, `setTimeout` would call it without arguments and `this=window` (assuming we're in the browser).

We still can pass the right `this` by using an intermediate variable, but that's a little bit more cumbersome:
Μπορούμε να περάσουμε το σωστό `this` χρησιμοποιώντας μια ενδιάμεση μεταβλητή, αλλά αυτή η τεχνική είναι λίγο πιο "δυσκίνητη":

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

return function(...args) {
let savedThis = this; // store this into an intermediate variable
let savedThis = this; // κράτησε αυτό σε μια ενδιάμεση μεταβλητή
setTimeout(function() {
f.apply(savedThis, args); // use it here
f.apply(savedThis, args); // χρησιμοποίησε το εδώ
}, ms);
};

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

---

# Delaying decorator
# Διακοσμητής καθυστέρησης

Create a decorator `delay(f, ms)` that delays each call of `f` by `ms` milliseconds.
Δημιούργησε ένα διακοσμητή `delay(f, ms)` που καθυστερεί κάθε κλήση της `f` χρόνο ίσο με `ms` δέκατα του δευτερολέπτου.

For instance:
Για παράδειγμα:

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

// create wrappers
// δημιουργία συναρτήσεων-καλύμματα
let f1000 = delay(f, 1000);
let f1500 = delay(f, 1500);

f1000("test"); // shows "test" after 1000ms
f1500("test"); // shows "test" after 1500ms
f1000("τέστ"); // δείχνει τέστ μετά από 1000 δέκατα του δευτερολέπτου
f1500("τέστ"); // δείχνει τέστ μετά από 1500 δέκατα του δευτερολέπτου
```

In other words, `delay(f, ms)` returns a "delayed by `ms`" variant of `f`.
Με άλλα λόγια, η `delay(f, ms)` επιστρέφει μια "καθυστερημένη κατά `ms`" εκδοχή της `f`.

In the code above, `f` is a function of a single argument, but your solution should pass all arguments and the context `this`.
Στον παραπάνω κώδικα, η `f` είναι μια συνάρτηση ενός μονού ορίσματος, αλλά η λύση σου πρέπει να περνάει όλα τα ορίσματα και το context `this`.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ function debounce(func, ms) {
};
}

```

Μια κλήση στη `debounce` επιστρέφει μια συνάρτηση-κάλυμμα. Μπορεί να υπάρχουν δύο καταστάσεις:

- `isCooldown = false` -- έτοιμη να τρέξει.
- `isCooldown = true` -- περιμένοντας για το timeout.

Στη πρώτη κλήση η `isCooldown` επιστρέφει μια τιμή λάθους, οπότε η κλήση προχωράει, και η κατάσταση αλλάζει σε `true`.

Όοσ η `isCooldown` είναι αληθής, όλες οι άλλες κλήσεις μπορούν να αγνοηθούν.

Τότε η `setTimeout` την ξανακάνει `false` μετά τη δοσμένη καθυστέρηση.


```

A call to `debounce` returns a wrapper. When called, it schedules the original function call after given `ms` and cancels the previous such timeout.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@ importance: 5

---

# Debounce decorator
# Debounce διακοσμητής

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.

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`).
Το αποτέλεσμα του `debounce(f, ms)` διακοσμητή πρέπει να είναι μια συνάρτηση-κάλυμμα η οποία περνάει τη κλήση στην `f` το πολύ μια φορά ανά `ms` δέκατα του δευτερολέπτου.

For instance, we had a function `f` and replaced it with `f = debounce(f, 1000)`.
Με άλλα λόγια, όταν καλούμε μια "debounced" συνάρτηση, μας εγγυάται ότι όλες οι μελλοντικές κλήσεις στη συνάρτηση που κάναν λιγότερο από `ms` δέκατα του δευτερολέπτου μετά τη προηγούμενη κλήση θα αγνοηθούν.

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.
Για παράδειγμα:

f(1); // τρέχει αμέσως
f(2); // αγνοείται

setTimeout( () => f(3), 100); // αγνοείται ( μόνο 100 ms περάσαν )
setTimeout( () => f(4), 1100); // τρέχει
setTimeout( () => f(5), 1500); // αγνοείται (λιγότερο από 1000 ms από το τελευταίο τρέξιμο)
```

Στη πρακτική η `debounce` είναι χρήσιμη για συναρτήσεις που ανακτούν/ανανεώνουν κάτι όταν γνωρίζουμε ότι τίποτα καινούργιο δεν μπορεί να γίνει σε μια τόσο μικρή περίοδο χρόνου, οπότε είναι καλύτερο να μην σπαταληθούν πόροι.

![](debounce.svg)

Expand Down Expand Up @@ -49,3 +58,4 @@ It waits the given time after the last call, and then runs its function, that ca
The task is to implement `debounce` decorator.

Hint: that's just a few lines if you think about it :)

Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ function throttle(func, ms) {
}
```

A call to `throttle(func, ms)` returns `wrapper`.
Μια κλήση στη `throttle(func, ms)` επιστρέφει το `wrapper`.

1. During the first call, the `wrapper` just runs `func` and sets the cooldown state (`isThrottled = true`).
2. In this state all calls are memorized in `savedArgs/savedThis`. Please note that both the context and the arguments are equally important and should be memorized. We need them simultaneously to reproduce the call.
3. After `ms` milliseconds pass, `setTimeout` triggers. The cooldown state is removed (`isThrottled = false`) and, if we had ignored calls, `wrapper` is executed with the last memorized arguments and context.
1. Κατά τη διάρκεια της πρώτης κλήσης, η `wrapper` απλά τρέχει τη `func` και θέτει τη κατάσταση παγώματος (`isThrottled = true`).
2. Στη κατάσταση αυτή όλες οι κλήσεις αποθηκεύονται στις `savedArgs/savedThis`. Παρακαλώ παρατήρησε ότι και το πλαίσιο και τα ορίσματα είναι το ίδιο σημαντικά και πρέπει να αποθηκευτούν. Τα θέλουμε ταυτόχρονα για να ξανακάνουμε τη κλήση.
3. Μετά από `ms` δέκατα του δευτερολέπτου, η `setTimeout` τρέχει. Η περίοδος παγώματος αφαιρείται (`isThrottled = false`) και, αν είχαμε αγνοημένες κλήσεις, η `wrapper` εκτελείται με τα τελευταία αποθηκευμένα ορίσματα και πλαίσιο.

The 3rd step runs not `func`, but `wrapper`, because we not only need to execute `func`, but once again enter the cooldown state and setup the timeout to reset it.
Το 3ο βήμα τρέχει όχι τη `func`, αλλά τη `wrapper`, γιατί δεν θέλουμε μόνο να εκτελέσουμε τη `func`, αλλά για άλλη μια φορά να μπούμε στη κατάσταση παγώματος και να θέσουμε το timeout για να το επαναφέρει.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ importance: 5

---

# Throttle decorator
# Throttle διακοσμητής


Δημιούργησε ένα "throttling" διακοσμητή `throttle(f, ms)` -- που επιστρέφει μια συνάρτηση κάλυμμα, δίνοντας τη κλήση στην `f` το πολύ μια φορά ανά `ms` δέκατα του δευτερολέπτου. Αυτές οι κλήσεις που πέφτου μέσα στη περίοδο "παγώματος", αγνοούνται.

**Η διαφορά με τη `debounce` -- αν μια κλήση που αγνοήθηκε είναι η τελευταία κατά το πάγωμα, τότε εκτελείται στο τέλος της καθυστέρησης.**

Create a "throttling" decorator `throttle(f, ms)` -- that returns a wrapper.

Expand All @@ -14,40 +19,44 @@ The difference with debounce is that it's completely different decorator:

In other words, `throttle` is like a secretary that accepts phone calls, but bothers the boss (calls the actual `f`) not more often than once per `ms` milliseconds.

Let's check the real-life application to better understand that requirement and to see where it comes from.

**For instance, we want to track mouse movements.**
Ας δουμε την πρακτική εφαρμογή για να καταλάβουμε καλύτερα την απαίτηση αυτή και να δούμε από που προέρχεται.

**Για παράδειγμα, θέλουμε να ιχνηλατήσουμε της κινήσεις του ποντικιού.**

In a browser we can setup a function to run at every mouse movement and get the pointer location as it moves. During an active mouse usage, this function usually runs very frequently, can be something like 100 times per second (every 10 ms).
**We'd like to update some information on the web-page when the pointer moves.**
Σε ένα δρομολογητή, μπορούμε να φτιάξουμε μια συνάρτηση να τρέχει σε κάθε κίνηση του ποντικιού και να παίρνουμε τη θέση του δείκτη όσο κινείται. Κατά τη διάρκεια μια ενεργούς χρήσης του ποντικιού, η συνάρτηση αυτή συνήθως τρέχει πολύ συχνά, μπορεί να είναι κάτι σαν 100 φορές το δευτερόλεπτο (κάθε 10 ms).

...But updating function `update()` is too heavy to do it on every micro-movement. There is also no sense in updating more often than once per 100ms.
**Θα θέλαμε να ανανεώσουμε κάποια πληροφορία στην ιστοσελίδα όταν ο δείκτης κινείται.**

So we'll wrap it into the decorator: use `throttle(update, 100)` as the function to run on each mouse move instead of the original `update()`. The decorator will be called often, but forward the call to `update()` at maximum once per 100ms.

Visually, it will look like this:
..Αλλα ανανεώνοντας τη συνάρτηση `update()` είναι πολύ βαρύ για να γίνεται σε κάθε μικρο-κίνηση. Επίσης, δεν υπάρχει λόγος να ανανεώνεται πιο συχνά από μια φορά ανά 100ms.

1. For the first mouse movement the decorated variant immediately passes the call to `update`. That's important, the user sees our reaction to their move immediately.
2. Then as the mouse moves on, until `100ms` nothing happens. The decorated variant ignores calls.
3. At the end of `100ms` -- one more `update` happens with the last coordinates.
4. Then, finally, the mouse stops somewhere. The decorated variant waits until `100ms` expire and then runs `update` with last coordinates. So, quite important, the final mouse coordinates are processed.
Οπότε θα το τυλίξουμε σε ένα διακοσμητή: χρησιμοποίησε το `throttle(update, 100)` σαν τη συνάρτηση για να τρέχει σε κάθε κίνηση του ποντικιού αντί για την αρχική `update()`. Ο διακοσμητής θα καλείται συχνά, αλλα θα προωθεί τη κλήση στην `update()` το πολύ μια φορά ανά 100ms.

A code example:
Οπτικά, θα μοιάζει κάπως έτσι:

1. Για τη πρώτη κίνηση του ποντικιού η διακοσμημένη εκδοχή αμέσως δίνει τη κλήσει στην `update`. Αυτό είναι σημαντικό, ο χρήστης βλέπει την αντίδραση μας στην κίνηση του αμέσως.
2. Μετά, όσο το ποντίκι κινείται, μέχρι τα `100ms` δεν γίνεται τίποτα. Η διακοσμημένη εκδοχή αγνοεί τις κλήσεις.
3. Στο τέλος των `100ms` -- μια ακόμα `update` γίνεται με τις τελευταίες συντεταγμένες.
4. Τότε, τελικά, το ποντίκι σταματάει κάπου. Η διακοσμημένη εκδοχή περιμένει μέχρι τα `100ms` περάσουν και μετά τρέχει την `update` με τις τελευταίες συντεταγμένες. Άρα, αρκετά σημαντικό είναι ότι, η τελευταίες συντεταγμένες του ποντικιού επεξεργάζονται.

Ένα παράδειγμα κώδικα:

```js
function f(a) {
console.log(a);
}

// f1000 passes calls to f at maximum once per 1000 ms
// f1000 περνάει τις κλήσεις στην f το πολύ μια φορά αν 1000 ms
let f1000 = throttle(f, 1000);

f1000(1); // shows 1
f1000(2); // (throttling, 1000ms not out yet)
f1000(3); // (throttling, 1000ms not out yet)
f1000(1); // δείχνει 1
f1000(2); // (throttling, 1000ms δεν έχουν περάσει)
f1000(3); // (throttling, 1000ms δεν έχουν περάσει)

// when 1000 ms time out...
// ...outputs 3, intermediate value 2 was ignored
// όταν 1000 ms περάσουν...
// ...εξάγει 3, η ενδιάμεση τιμή 2 αγνοείται
```

P.S. Arguments and the context `this` passed to `f1000` should be passed to the original `f`.
Υ.Σ. Ορίσματα και το πλαίσιο `this` που δίνεται στην `f1000` πρέπε να περαστεί στην αρική συνάρτηση `f`.

Loading