de Konstantin Blokhin

Cum să evitați poluarea de verificare nulă în JavaScript: utilizați opționalele

Cum sa evitati poluarea de verificare nula in JavaScript utilizati
Spălați-vă codul! de Charlie Chauvin

Folosesc JavaScript în ultimii ani și mă bucur de el în general. Dar îi lipsesc unele caracteristici interesante din alte limbi. De exemplu, nu există o navigație sigură încorporată și nu există mijloace pentru a evita verificările nule. Codul se poluează cu ramuri condiționate ale plăcii centrale. Este predispus la erori și este mai puțin lizibil.

Ce este în neregulă cu verificările nule, ați putea întreba?

În primul rând, inventatorul referinței nule, Tony Hoare, numit este un greșeală de miliarde de dolari:

Dar nu am putut rezista tentației de a introduce o referință nulă, pur și simplu pentru că a fost atât de ușor de implementat. Acest lucru a dus la nenumărate erori, vulnerabilități și blocări ale sistemului, care au cauzat probabil un miliard de dolari de durere și daune în ultimii patruzeci de ani..

JavaScript nu este diferit. De câte ori ai întâlnit TypeError: Cannot read property 'bar' of null eroare? Pentru a le evita, dezvoltatorii trebuie întotdeauna să o facă rețineți această posibilitate nulă. Și ar prefera să se concentreze asupra faptului real, cum ar fi logica specifică aplicației.

Apoi, trebuie să introduceți noi ramuri condiționale în cod. Și, în general, nu vrei să ai multe, de atunci if declarațiile tind să scadă lizibilitatea generală a codului. Ia săgeată anti-model ca exemplu extrem. Mai mult, aceste afirmații sunt aproape fără sens în ceea ce privește domeniul dvs. sau logica de afaceri.

Îmi place unul dintre modurile de a rezolva problema respectivă, explicat aici de Michael Feathers – abordarea „comenzi în loc de interogări”. Și el dezvoltă, de asemenea, această idee și vorbește despre beneficiile codului necondiționat în general.

Practic, în loc să interogăm o bucată de date și să verificăm dacă există pentru o prelucrare ulterioară, ne exprimăm intenția și lăsăm internele modulului să decidă dacă ar trebui sau nu acțiunea.

Să presupunem că vrem să aducem cărțile preferate ale unui utilizator și ale prietenilor lor pentru recomandare. Codul ar putea fi ca:

Deci, aș prefera să am un modul care să încapsuleze logica de verificare. Prin urmare, îi spunem doar ce să facă cu un utilizator activ:

Codul este acum mai concentrat pe logica afacerii. Și îl putem reutiliza pentru a efectua alte acțiuni cu un utilizator activ fără sarcina verificărilor nule.

Dar soluția dată este un pic prea specifică. În plus, avem încă o verificare nulă în controlerul nostru sau în alt loc în care folosim funcția.

Este necesară o abordare mai generală. Avem nevoie de un tip de date special, un container pentru valori nulabile – cum ar fi Opțional clasa în Java, de exemplu. Ideea este că orice acțiune, dată containerului, va fi executată numai pe o valoare care nu conține goluri.

Există unele biblioteci JS (cum ar fi Optional.js), implementând aproape aceeași interfață ca Java Optional. Dar nu țin cont de natura asincronă a JS și nu funcționează cu Promisiunile.

Și de cele mai multe ori când este posibilă absența unei valori, trebuie să ne ocupăm de fapt de promisiuni și funcții asincrone. De exemplu, luați cereri de resurse externe, cum ar fi interogări de baze de date și apeluri API.

Asta e cand AsyncOptional vine în ajutor. Asa de, este un container pentru o valoare opțională de natură asincronă. Opțional înseamnă că valoarea poate fi prezentă sau absentă. Ambii null și undefined sunt considerate absente.

De îndată ce vrem să spunem programului ce trebuie să facem cu o valoare care nu este goală, metoda din fabrică se numește „cu”:

const withUser = AsyncOptional.with(user);

Apoi am putea face unele procesări, așa cum se arată în exemplul de mai jos. După ce am terminat și dorim să remediem rezultatul undeva, ar trebui utilizată una dintre metodele terminale.

De exemplu, atunci când nu trebuie să reacționăm la o valoare goală:

De asemenea, putem specifica ce să facem în cazul absenței valorii în acest mod asemănător limbajului natural:

Între apelurile din fabrică și metoda terminalului, poate exista orice logică de procesare, descrisă în Citește-mă. Este garantat că nu va fi luată nicio acțiune asupra unei valori goale.

Unele dintre acțiunile pe care le puteți utiliza pentru a procesa valoarea:

Deci, să aruncăm o privire finală asupra exemplului nostru:

Deci, cum este mai bine în comparație cu cea inițială?

Cred că răspunsul este – este așa mai curat și mai ușor de citit, iar lizibilitatea este esența unui cod bun.

În primul rând, am scăpat de verificarea nulă a ramurilor condiționate, astfel încât să ne putem concentra asupra lucrului important – care este logica de afaceri a sistemului.

Apoi, am eliminat posibilitatea de a avea excepții ale indicatorului nul aici. Aceasta înseamnă că nu trebuie să ținem cont de nulitatea valorii, care este un mod mai puțin de a introduce bug-uri.

Un alt caz în care biblioteca poate fi utilă este într-o situație de „valoare implicită”. Să presupunem că avem un fel de formular pe care un utilizator îl poate completa, iar printre alte câmpuri există selecția fructelor. Utilizatorul poate alege un portocaliu, un măr sau nimic.

Deci rezultatul este:

conditionalChooseFruit(‘Joe’);// => You chose nothing, Joe.
conditionalChooseFruit(‘Joe’, {notFruit: ‘x’});// => You chose nothing, Joe.
conditionalChooseFruit(‘Joe’, {fruit: 1});// => You chose apple, Joe.
conditionalChooseFruit(‘Joe’, {fruit: 11});// => You chose a wrong gardener to mess with, Joe.

Pentru mine, această metodă arată cam dezordonată chiar și cu ajutorul condițiilor asincronizate / așteptate și inversate. Logica afacerii devine estompată de ramurile condiționate.

Și cu AsyncOptional poate fi rescris într-un mod mai simplu:

Nu este mai ușor de citit?

Asa ca AsyncOptional biblioteca vă poate ajuta să:

  • scrieți codul pe care îl puteți găsi mai lizibil, mai ușor de întreținut, mai curat și mai frumos;
  • evitați null-related TypeErrors mai bine, crescând astfel stabilitatea sistemului dvs.;
  • funcționează cu Promisiuni și funcții asincrone în același mod curat (și funcționează și cu cele sincrone).

Dacă v-au plăcut exemplele, vă rugăm să nu ezitați să explorați Citește-mă fișier în GitHub repo sau chiar verificați completul Documente API. Îndrăznesc chiar să sugerez că puteți instala pachet prin npm. 🙂 Aș aprecia, de asemenea, orice feedback.

asincron-opțional
Implementare opțională cu suport asincronwww.npmjs.com