|
| 1 | +### `setTimeout` i `setInterval` |
| 2 | + |
| 3 | +Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania |
| 4 | +funkcji korzystając z funkcji `setTimeout` i `setInterval`. |
| 5 | +Since JavaScript is asynchronous, it is possible to schedule the execution of a |
| 6 | +function by using the `setTimeout` and `setInterval` functions. |
| 7 | + |
| 8 | +> **Note:** Funkcje czasowe nie są częścią standardu ECMAScript. Jest to część |
| 9 | +> standardu [DOM][1]. |
| 10 | +
|
| 11 | + function foo() {} |
| 12 | + var id = setTimeout(foo, 1000); // zwraca licznę typu Number > 0 |
| 13 | + |
| 14 | +Powyższe wywołanie `setTimeout` zwraca ID budzika i planuje wywołanie `foo` za |
| 15 | +**około** tysiąc milisekund. `foo` zostanie wykonana dokładnie **jeden raz**. |
| 16 | + |
| 17 | +**Nie ma pewności**, że kod zaplanowany do wykonania wykona się dokładnie po |
| 18 | +upłynięciu zadanego czasu podanego jako parametr do `setTimeout`, ponieważ zależy |
| 19 | +to od dokładności zegara w silniku JavaScript, który wykonuje kod oraz od tego, |
| 20 | +że inny kawałek kodu może zablokować wątek, ponieważ JavaScript jest tylko |
| 21 | +jedno wątkowy. |
| 22 | + |
| 23 | +Funkcja, która została przekazana jako pierwszy parametr zostanie wykonana w |
| 24 | +globalnym zasięgu, co oznacza, że [`this`](#function.this) wewnątrz tej funkcji |
| 25 | +będzie wkazywać na obiekt *global*. |
| 26 | + |
| 27 | + function Foo() { |
| 28 | + this.value = 42; |
| 29 | + this.method = function() { |
| 30 | + // this wskazuje na obiekt global |
| 31 | + console.log(this.value); // wypisze undefined |
| 32 | + }; |
| 33 | + setTimeout(this.method, 500); |
| 34 | + } |
| 35 | + new Foo(); |
| 36 | + |
| 37 | +> **Uwaga:** Ponieważ `setTimeout` przyjmuje **obiekt funkcji** jako pierwszy |
| 38 | +> argument często popełnianym błędem jest wykorzystanie składni `setTimeout(foo(), 1000)`, |
| 39 | +> która użyje wartości zwróconej przez funkcję `foo` jako parametru zamiast |
| 40 | +> funkcji `foo` samej w sobie. W większości przypadków będzie to cichy błąd, |
| 41 | +> ponieważ jeżeli funkcja zwróci `undefined` `setTimeout` **nie** wyrzuci żadnego |
| 42 | +> błędu. |
| 43 | +
|
| 44 | +### Kolejkowanie wywołań z `setInterval` |
| 45 | + |
| 46 | +Podczas, gdy `setTimeout` wywołuje podaną funkcję tylko raz, `setInterval` - |
| 47 | +jak wskazuje nazwa - będzie wykonywać funkcję **w odstępach czasowych** co `X` |
| 48 | +milisekund. Jednakże korzystanie z tej funkcji jest odradzane. |
| 49 | + |
| 50 | +Kiedy wykonywany kod zablokuje możliwość uruchomienia zaplanowanej funkcji, |
| 51 | +`setInterval` będzie próbować uruchamiać daną funkcję co będzie powodować |
| 52 | +kolejkowanie wykonania tej samej funkcji kilkakrotnie. W szczególności może się |
| 53 | +to wydarzyć przy krótkim interwale. |
| 54 | + |
| 55 | + function foo(){ |
| 56 | + // coś co blokuje wykonanie na 1 sekundę |
| 57 | + } |
| 58 | + setInterval(foo, 100); |
| 59 | + |
| 60 | +W powyższym kodzie kod `foo` zostanie wywołany tylko raz i zablokuje wywołanie na |
| 61 | +jedną sekundę. |
| 62 | + |
| 63 | +Podczas, gdy funkcja `foo` blokuje wykonanie `setInterval` będzie planować kolejne |
| 64 | +wywołania `foo`. W momencie, gdy pierwsze wywołanie `foo` się zakończy, już |
| 65 | +w kolejce do wywołania będą czekały kolejne **dziesięć** wywołań tej funkcji. |
| 66 | + |
| 67 | +### Radzenie sobie z możliwymi blokadami |
| 68 | + |
| 69 | +Najprostrzym jak również najbardziej kontrolowaną sytuacją jest użycie `setTimeout` |
| 70 | +wewnątrz wywoływanej funkcji. |
| 71 | + |
| 72 | + function foo(){ |
| 73 | + // coś co blokuje wykonanie na 1 sekundę |
| 74 | + setTimeout(foo, 100); |
| 75 | + } |
| 76 | + foo(); |
| 77 | + |
| 78 | +Powyższy kod nie tylko hermetyzuje wywołanie `setTimeout` ale również zapobiega |
| 79 | +kolejkowaniu wywołań fukcji i daje dodatkową kontrolę. W tym przypadku funkcja |
| 80 | +`foo` może zdecydować czy powinna się wywołać ponownie czy też nie. |
| 81 | + |
| 82 | +### Ręczne usuwanie budzików |
| 83 | + |
| 84 | +Usuwanie budzików i interwałów dokonywane jest przez przekazanie odpowiedniego ID |
| 85 | +do `clearTimeout` lub `clearInterval`, w zależności z jakiej funkcji zostało |
| 86 | +zwrócone ID. |
| 87 | + |
| 88 | + var id = setTimeout(foo, 1000); |
| 89 | + clearTimeout(id); |
| 90 | + |
| 91 | +### Usuwanie wszystkich budzików |
| 92 | + |
| 93 | +Ponieważ nie istnieje wbudowana metoda na usunięcie wszystkich budzików i/lub |
| 94 | +interwałów, konieczne jest użycie metody brute force aby osiągnąć ten efekt. |
| 95 | + |
| 96 | + // usunięcie "wszystkich" budzików |
| 97 | + for(var i = 1; i < 1000; i++) { |
| 98 | + clearTimeout(i); |
| 99 | + } |
| 100 | + |
| 101 | +Nadal może istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała, |
| 102 | +ponieważ ID było z innego przedziału, dlatego zamiast korzystania z metody brute |
| 103 | +force, zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć. |
| 104 | + |
| 105 | +### Ukryte wykorzystanie `eval` |
| 106 | + |
| 107 | +Do `setTimeout` i `setInterval` można również przekazać string jako pierwszy |
| 108 | +parametr zamiast obiektu funkcji, jednakże **nigdy** nie należy korzystać z tej |
| 109 | +możliwości, ponieważ wewnętrznie `setTimeout` i `setInterval` wykorzystują `eval`. |
| 110 | + |
| 111 | +> **Uwaga:** Ponieważ funkcje budzików **nie** są częścią specyfikacji standardu |
| 112 | +> ECMAScript, działanie tych funkcji nie jest określone w momencie, gdy zostanie |
| 113 | +> do nich przekazany string. Na przykład Microsoftowy JScript wykorzystuje |
| 114 | +> konstruktor `Function` zamiast funkcji `eval`. |
| 115 | +
|
| 116 | + function foo() { |
| 117 | + // zostanie wykonane |
| 118 | + } |
| 119 | + |
| 120 | + function bar() { |
| 121 | + function foo() { |
| 122 | + // nigdy nie zostanie wywołane |
| 123 | + } |
| 124 | + setTimeout('foo()', 1000); |
| 125 | + } |
| 126 | + bar(); |
| 127 | + |
| 128 | +Ponieważ `eval` nie zostało wywołane [wprost](#core.eval) w tym przypadku, to |
| 129 | +string przekazany do `setTimeout` zostanie uruchomiony w *zasięgu globalnym*, |
| 130 | +co za tym idzie lokalna zmienna `foo` z zasięgu `bar` nie zostanie użyta. |
| 131 | + |
| 132 | +Kolejnym zaleceniem jest aby **nie** stosować stringów do przekazywania argumentów |
| 133 | +do funkcji, która ma zostać wywołana przez budzik. |
| 134 | + |
| 135 | + function foo(a, b, c) {} |
| 136 | + |
| 137 | + // NIGDY nie należy tak robić |
| 138 | + setTimeout('foo(1,2, 3)', 1000) |
| 139 | + |
| 140 | + // Zamiast tego należy skorzystać z anonimowej funkcji |
| 141 | + setTimeout(function() { |
| 142 | + foo(a, b, c); |
| 143 | + }, 1000) |
| 144 | + |
| 145 | +>**Uwaga:** Mimo, że możliwe jest wykorzystanie składni |
| 146 | +> `setTimeout(foo, 1000, a, b, c)`, to nie zaleca się korzystania z niej, ponieważ |
| 147 | +> może to prowadzić do subtelnych błędów podczas wykorzystania [metod](#function.this). |
| 148 | +
|
| 149 | +### Wnioski |
| 150 | + |
| 151 | +Nie należy **nigdy** przekazywać stringu jako parametru do `setTimeout` lub |
| 152 | +`setInterval`. Jest to wyraźną oznaką **bardzo** złego kodu, jeżeli potrzebne jest |
| 153 | +przekazanie argumentów do funkcji należy skorzystać z *anonimowej funkcji* i |
| 154 | +wewnątrz niej dokonać przekazania argumentów. |
| 155 | + |
| 156 | +Ponadto, należy unikać korzystanie z `setInterval`, ponieważ planista może |
| 157 | +zablokować wykonanie JavaScriptu. |
| 158 | + |
| 159 | +[1]: http://pl.wikipedia.org/wiki/Obiektowy_model_dokumentu "Document Object Model" |
| 160 | + |
0 commit comments