Novi ES2021 Javascript features

. . .
image

NOTE: Svi ovi ES2021 featuri su dostupni u večini večih browsera, dok su ostali u procesu dodavanja. Tako da sve ove primjere možete isprobati u bilo kojem većem browseru, ali ja preporučujem Brave i Google Chrome

Prije nego što počnemo moram spomenuti da iako su ovi featuri dodani u browsere i možemo ih koristiti, i na dan kada pišem ovaj blog 24 Aprila, 2021 nisu postali dio standarda Javascript. To bi se trebalo desiti u Junu 2021 kada bi trebali biti dodani u standard ECMAScript (EDIT: U Junu 2021 svi ovi feturi su dodani u standard EMCAScript) Ako želite da vidite i pročitate o ovim featurima ili o novim, možete posjetiti oficijelni GitHub repository gdje se ovi featuri prezentuju, diskutuju i odobravaju za dodavanje u Javascript TC39.

Imamo pet novih featura u Javascript a oni su:

  • String.prototype.replaceAll()
  • Promise.any()
  • Logical Assignment Operators
  • Numeric spearators
  • WeakRefs

String.prototype.replaceAll()

Prvi je replaceAll() koji možemo koristiti da zamijenimo sve instance u stringu. Već imamo sličnu funkciju replace() koja mijenja samo prvu instancu ali ne sve. Rješenje za to je bilo koristenje regexa. Ali već znamo da je regex zbunjijući a i sintaksa je čudna. Također ne koristimo regex toliko često tako da moramo osvježiti naše pamćenje prije nego ga koristimo, ili da kopiramo neko rješenje sa interneta koje nam može stvoriti probleme ako ne razumijemo šta tačno radi to. Tako da je replaceAll() odličan dodatak u Javascript, tako da se ne moramo mučiti sa regex. Evo primjer kako možemo koristiti replaceAll():

1const text = 'This text should be replaced with a different text';
2
3const replaceFn = text.replace('text', 'name');
4
5const replaceAllFn = text.replaceAll('text', 'name');
6
7console.log(replaceFn);
8// This name should be replaced with a different text
9
10console.log(replaceAllFn);
11// This name should be replaced with a different name

Ako probate ovaj primjer iznad, vidjet će te da replaceFn mijenja samo prvu instancu riječi "text", ali funkcija replaceAllFn mijenja sve instance riječi "text" u stringu. Kao što možete vidjeti ovaj feature je dobar i korista, ne moramo više da radimo sa regex barem za ovakve stvari 🎉. Ukoliko želite detaljnije da provjerite ovaj feature, možete posjetiti oficijelni GitHub repo gdje se ovi feturi prezentuju i diskutuju String.prototype.replaceAll().

Promise.any()

Slijedeći feature je dodan u Promise metode i prima listu promisa Promise.any([first, second, third]), i onda čekamo prvi uspješni promise ili sve promise da ne uspiju. Ako je first promise uspješan, prekinemo ostale promise, ali ako je prvi neuspješan a second je uspješan, onda prekidamo promise nakon second. Isto važi ako su first i second neuspješni ali third je uspješan, prekidamo nakon third. Ovo je optimistični Promise, koji se neče prekinuti sve dok barem jedan uspije, ili dok svi neuspiju. Sada ću da pokažem na primjeru kako da se koristi, i kako se razlikuje od ostalih Promise metoda:

1// We use delay to simulate response from an API
2const delay = () => Math.floor(Math.random() * 1000);
3
4const first = new Promise((resolve, reject) => {
5 setTimeout(() => resolve('First'), delay());
6});
7
8const second = new Promise((resolve, reject) => {
9 setTimeout(() => resolve('Second'), delay());
10});
11
12const third = new Promise((resolve, reject) => {
13 setTimeout(() => resolve('Third'), delay());
14});
15
16const promiseAny = async () => {
17 try {
18 const firstResponse = await Promise.any([first, second, third]);
19
20 //First one to be successfull will be the value of firstResponse
21 console.log(firstResponse);
22 } catch (error) {
23 console.log(error);
24 }
25};
26
27promiseAny();

U ovom primjeru svi se promisi izvršavaju, i prvi koji se izvrši bit ce dodan u firstResponse. Ali ako jedan od njih ili dva se ne izvrši, onda čemo dobiti onaj koji se uspješno izvrši:

1// We use delay to simulate response from an API
2const delay = () => Math.floor(Math.random() * 1000);
3
4const first = new Promise((resolve, reject) => {
5 setTimeout(() => reject('First'), delay());
6});
7
8const second = new Promise((resolve, reject) => {
9 setTimeout(() => reject('Second'), delay());
10});
11
12const third = new Promise((resolve, reject) => {
13 setTimeout(() => resolve('Third'), delay());
14});
15
16const promiseAny = async () => {
17 try {
18 const firstResponse = await Promise.any([first, second, third]);
19
20 //Third one will be the value of firstResponse
21 console.log(firstResponse);
22 } catch (error) {
23 console.log(error);
24 }
25};
26
27promiseAny();

Ovdje imamo first i second neuspješan promise. Ali third je uspješno izvršen, zato ćemo uvijek dobiti third kao rezultat Promise. A ako su svi neuspješni Promise, onda ćemo dobiti grešku koja kaže AggregateError: All promises were rejected. Evo primjer tog slučaja:

1// We use delay to simulate response from an API
2const delay = () => Math.floor(Math.random() * 1000);
3
4const first = new Promise((resolve, reject) => {
5 setTimeout(() => reject('First'), delay());
6});
7
8const second = new Promise((resolve, reject) => {
9 setTimeout(() => reject('Second'), delay());
10});
11
12const third = new Promise((resolve, reject) => {
13 setTimeout(() => reject('Third'), delay());
14});
15
16const promiseAny = async () => {
17 try {
18 const firstResponse = await Promise.any([first, second, third]);
19
20 //This will not be executed since our Promise throws error
21 console.log(firstResponse);
22 } catch (error) {
23 // Here we will get our error: AggregateError: All promises were rejected
24 console.log(error);
25 }
26};
27
28promiseAny();

Ovdje možete pročitati više o ovom featuru Promise.any()

1// Here we wait for all promises to finish but if one fails, we throw an error
2Promise.all([first, second, third]);
3
4// Here we wait for first promise to finish, failed or sucessfull
5Promise.race([first, second, third]);
6
7// Here we wait for all promises to finish, and array of all promises is returned
8// with their status and values or reason if it's rejected
9Promise.allSettled([first, second, third]);
10
11//Here we wait for the first that is succefull or all of them failed
12Promise.all([first, second, third]);

And here is where you can read more about this feature Promise.any()

Logical Assignment Operators

Ovaj feature je inspirisan Ruby programskim jezikom, i koristimo ga da pisemo uslovne provjere u kraćim formama. Ovdje nemamo baš puno za objasniti jer ove feature smo već imali u Javascript, samo su sada napravljeni da se koriste u kraćem obliku. Evo primjer kako se koriste:

1// x = x || 0
2// x ||= 0
3// x = x ?? 0
4// x ??= 0
5// x = x && 0
6// x &&= 0
7
8const multiply = (dot) => {
9 dot.x ??= 2;
10 dot.y ??= 2;
11 return dot;
12};
13multiply({ x: 0 }); // 4
14
15
16Za **dot.x ??=2**, ako **dot.x** nije `null` ili `undefiend` ostavit ćemo vrijednost koja je bila u **dot.x**, ali ako je njegova vrijednost `null` ili `undefiend` onda ćemo dodijeliti vrijednost 2. Ovaj znak ?? se zove **Nullish Coalescing operator** i on provjerava da li su vrijednosti `null` or `undefined`. On je dodan u ES2020.
17
18Ako želite da pročitate više o ovom feature, posjetite oficijelni GitHub repo [Logical Assignment Operators](https://github.com/tc39/proposal-logical-assignment).
19
20## Numeric separators
21
22Ovaj feature je dodan u Javasript da bi olakšao čitanje brojeva. Ako imamo veliki broj, teško je na prvu vidjeti koji je tačno broj, pogotovo kada imamo puno cifri. Ukoliko napišemo veliku broj, i teško je na prvu pročitati koja veličina je upitanju, mozemo koristiti ovaj feature da razdvojimo, desetine, hiljade ...
23Primjer kako koristimo:
24
25```js
26const largeNumer = 12000000000;
27
28const largeNumberFormated = 12_000_000_000;

Vidite da pomogne kod čitanja brojeva kao što su milioni, milijarde, trilioni. Za detaljnije informacije posjetite GitHub repo Numeric separators

WeakRefs

Prije nego kažem bilo šta o ovom feature, moram da kažem da je mogućnost da ga koristimo je veoma mala. Ovo je malo veći nivo Javascripta, ali pokušat ću ga objasniti svakako, možda ga budemo koristili jedan dan 🤣.

I Javscript imamo Garbage Collectore(GC), što znaći da Javascript engine čisti nekorištene vrijednosti tako da bi oslobodio memoriju i možda stavio nešto drugo na ta mjesta. Znači ako imamo variablu let person kojoj je dodan neki objekat, ona ima jaku referencu na taj objekat, a to znači da taj objekat neće biti obrisan od starne Garbage Collectora, i neće biti izbrisan iz memorije.

Ako imamo objekat u moemoriji na kojeg niko ne pokazuje, onda postoji mogučnost da će biti pokupljen sa GC. A zašto kažem možda je zbog non-obvious načina na koji Javascript engine radi, jer ne zna se tačno kada će se desiti GC, i da li će se desiti uopšte, to engine sam odlučuje u kojem trenutku će se izvršiti GC. Također imamo više verzija Javscript engina i večina njih radi drugaćije, tako da Google Crome V8 će imati drugačiji GC od Firefox koji koristi Spider monkey engine.

NOTE: U Javascriptu samo Object i Strings su Garbage Collected!

WeakRefs su slabe reference na objekte u memoriji, to znaći da nemamo jaku referencu na taj objekat i ukoliko dođe do GC onda nećemo pokazivati ni na šta, i varijabla kojoj je dodan WeakRef će postati undefiend. Mi možemo provjeriti da li WeakRef ima vrijednost tako što ćemo pozvati deref() funkciju i ako dobijemo undefiend znaći da je vrijednost varijable uklonjena sa GC, ali ako dobijemo vrijednost to znaći da je objekat još uvijek u memoriji i da ga nije uklonio Garbage Collector. Primjer kako to možemo uraditi:

1const value = {
2 name: 'Vehid Trtak',
3 city: 'Sarajevo'
4};
5
6//Create weak reference to the value
7const weakRef = new WeakRef(value);
8
9//Check if values still exists
10const maybeValue = weakRef.deref();
11if (maybeValue === undefiend) {
12 // Value was removed by GC
13} else {
14 // Value is still available
15}

Imamo još jednu stvar koju možemo koristiti sa WeakRefs a to je FinalizationRegistry. Koristeći ovu funkciju dobit ćemo notifikaciju kada naša vrijednost bude uklonjena sa Garbage Collectorom, tako da ne moramo stalno pozivati deref() da provjerimo da li objekat još uvijek postoji. Ovaj code pokazuje kako to možemo uraditi:

1const notify = new FinalizationRegistry((value) => {
2 // Do something when you get notified
3});

NOTE: Kada FinalizationRegistry callback se izvrši to znači da je vrijednost već izbrisana i da ne postoji više u memoriji!

Pošto je ovo kompleksinja tema, a i ljudi koji su predložili da ovaj feature bude dodan u Javascript govore da ne koristimo ovo osim ako znamo šta se tačno dešava. Tako da neću dalje objašnjavati ovu temu, ako želite možete više pročitati na GitHub repo WeakRefs and FinalizationRegistry.

Zaključak

Prošli smo kroz pet novih dodataka, neke od njih su korisne, neka ne baš toliko. Nadam se da vam je ovaj post bio koristan, i ako možete podjieliti i sa drugim da i oni mogu naučiti o ovome.