Sulla Programmazione

Quattro chiacchere sulla programmazione e sulle bit-tecnologie con Fabrizio Cipriani

Il signor Javascript, il dottor $.Deferred e la Piramide della Sventura

E' difficile parlare della programmazione Javascript senza discutere delle tecniche di programmazione asincrona.

La programmazione asincrona

La programmazione asincrona si rende necessaria quando scriviamo del codice che blocca l'I/O per lunghi periodi (un esempio a caso, Ajax), oppure quando usiamo i timers. In questi casi tipicamente si indica al sistema qual'è l'operazione da eseguire e gli si passa una funzione "callback", da chiamare ad operazione terminata. Il sistema si prende l'incarico di portare a termine il compito e ritorna immediatamente il controllo, lasciandoci la certezza che le callback che gli abbiamo passato saranno alla fine invocate.

Ad esempio (usiamo jQuery):

La chiamata ajax $.get() prende in carico l'operazione in modo asincrono, il flusso prosegue visualizzando "Messaggio 2" e poco dopo, quando l'operazione termina, viene visualizzato "Messaggio 1".

E' quando abbiamo la necessità di eseguire operazioni sequenziali in un contesto asincrono che nascono i primi mal di testa.

Facciamo un esempio, mettiamo di dover eseguire queste operazioni:

  • Richiedere la url di un servizio web esterno al nostro server
  • invocare il servizio che risponde alla url
  • Passare il risultato al nostro server, il quale lo elabora e ci restituisce il risultato

Tireremmo fuori qualcosa del genere:

Poichè ogni operazione dipende dal risultato della precedente, siamo costretti ad annidare le callback. Le indentazioni si susseguono pericolosamente creando quella che è oramai conosciuta con il termine di "Piramide della sventura" (Pyramid of Doom).

Le promesse

In nostro aiuto accorre il pattern "Promise" (Promessa).

Le librerie che implementano il pattern permettono di creare un oggetto che agisce come contenitore di una operazione il cui risultato non è inizialmente conosciuto, ma che sarà alla fine sicuramente disponibile.

Una volta attivato, l'oggetto "promette" di eseguire l'operazione il prima possibile, di invocare le funzioni callback che corrispondono al successo o al fallimento dell'operazione, e ritorna il controllo in modo asincrono.

Il fatto che tutta la logica sia racchiusa in un oggetto programmabile apre interessanti possibilità, come quella di assegnare all'oggetto multiple callbacks in momenti differenti (saranno eseguite nell'ordine in cui vengono assegnate), oppure quella di passarlo in giro come parametro di altre funzioni.

Il dottor $.Deferred

jQuery implementa le promesse con l'oggetto $.Deferred. $.Deferred viene inizializzato con una funzione (l'operazione). Successivamente è possibile definire la callback da invocare quando l'operazione termina con successo e quella per l'errore (entrambe come primo e secondo parametro del metodo then()).

All'interno dell'oggetto $.Deferred, noi stessi dobbiamo decidere se la nostra operazione ha avuto successo chiamando il metodo resolve(), o se è avvenuto un errore chiamando reject().

Ad esempio:

Nell'esempio, l'oggetto $.Deferred viene inizializzato con una funzione che esegue una semplice operazione e poi termina con successo.

Immediatamente dopo usiamo il metodo "then()" per definire la funzione da invocare in caso di successo.

Quando chiamiamo resolve(), viene invocata la funziona definita con il metodo then(). Eseguendo il codice dell'esempio, abbiamo la garanzia che i due messaggi vengano visualizzati nell'ordine corretto.

E' importante sapere che le funzioni passate a "then()" vengono eseguite ANCHE se assegnate dopo che l'operazione è terminata (in quel caso vengono eseguite immediatamente).

Il primo vantaggio delle promesse è quello di permettere al signor Javascript di mettersi immediatamente alle spalle gli orrori della Piramide della Sventura.

Nell'esempio successivo, bisogna premettere che "then()" può ritornare un oggetto $.Deferred (dalla versione 1.8 di jQuery), e che in jQuery tutti i metodi Ajax ritornano anch'essi un oggetto $.Deferred.

Sapendo questo, possiamo riscrivere la nostra piramide in questo modo:

Mettiamo invece di dovere aspettare che tutti i metodi asincroni siano eseguiti prima di eseguire il nostro codice. Questo è possibile usando il metodo when():

Conclusione

Le promesse sono uno strumento così potente in Javascript (in tutti i casi di programmazione concorrenziale e asincrona, in effetti), che oggi praticamente non esiste blogger che non ne abbia scritto (si, ieri ce n'era ancora uno).

Come dice uno dei più conosciuti articoli sull'argomento, "oggi oramai praticamente tutti i programmatori javascript e le loro nonne hanno sentito parlare delle Promesse".

Le promesse sono uno strumento potente, praticamente indispensabile per qualsiasi sviluppatore che scriva codice Javascript client-server, e non solo!

Comments