Qual è la promise esplicita di costruzione antipattern e come posso evitarlo?

Stavo scrivendo codice che fa qualcosa che assomiglia a:

function getStuffDone(param) { | function getStuffDone(param) { var d = Q.defer(); /* or $q.defer */ | return new Promise(function(resolve, reject) { // or = new $.Deferred() etc. | // using a promise constructor myPromiseFn(param+1) | myPromiseFn(param+1) .then(function(val) { /* or .done */ | .then(function(val) { d.resolve(val); | resolve(val); }).catch(function(err) { /* .fail */ | }).catch(function(err) { d.reject(err); | reject(err); }); | }); return d.promise; /* or promise() */ | }); } | } 

Qualcuno mi ha detto che questo è chiamato rispettivamente ” differito antipattern ” o ” Promise constructor antipattern “, cosa c’è di male in questo codice e perché questo è chiamato antipattern ?

L’ antipattern differito (ora l’anti-modello esplicito della costruzione) coniato da Esailija è un popolo anti-modello comune che è nuovo alle promesse fatte, l’ho fatto io stesso quando ho usato le promesse per la prima volta. Il problema con il codice precedente è che non riesce a utilizzare il fatto che promette catena.

Le promesse possono concatenare e quindi è ansible restituire le promesse direttamente. Il tuo codice in getStuffDone può essere riscritto come:

 function getStuffDone(param){ return myPromiseFn(param+1); // much nicer, right? } 

Le promesse consistono nel rendere più leggibile il codice asincrono e comportarsi come il codice sincrono senza nasconderlo. Le promesse rappresentano un’astrazione su un valore di un’operazione singola, astraggono la nozione di una dichiarazione o espressione in un linguaggio di programmazione.

È necessario utilizzare gli oggetti posticipati solo quando si converte un’API in promesse e non è ansible farlo automaticamente o quando si scrivono funzioni di aggregazione che sono più facili da esprimere in questo modo.

Citando Esailija:

Questo è l’anti-pattern più comune. È facile cadere in questo quando non si capiscono le promesse e le si pensa come emettitori di eventi glorificati o utilità di callback. Ricapitoliamo: le promesse riguardano il fatto che il codice asincrono mantenga la maggior parte delle proprietà perse del codice sincrono come l’indentazione piatta e un canale di eccezione.

Che cosa c’è che non va?

Ma il modello funziona!

Sei fortunato. Sfortunatamente, probabilmente no, dato che probabilmente hai dimenticato un caso limite. In più della metà delle occorrenze che ho visto, l’autore ha dimenticato di occuparsi del gestore degli errori:

 return new Promise(function(resolve) { getOtherPromise().then(function(result) { resolve(result.property.example); }); }) 

Se l’altra promise viene respinta, ciò avverrà inosservato invece di essere propagato alla nuova promise (dove verrebbe gestito) – e la nuova promise rimarrà in sospeso per sempre, il che può indurre fughe.

La stessa cosa accade nel caso in cui il codice di richiamata causi un errore, ad esempio quando il result non ha una property e viene generata un’eccezione. Ciò andrebbe non gestito e lascerà la nuova promise irrisolta.

Al contrario, l’utilizzo di .then() occupa automaticamente di entrambi questi scenari e rifiuta la nuova promise quando si verifica un errore:

  return getOtherPromise().then(function(result) { return result.property.example; }) 

L’antipattern differito non è solo ingombrante, ma anche sobject a errori . L’uso di .then() per concatenare è molto più sicuro.

Ma ho gestito tutto!

Veramente? Buona. Tuttavia, questo sarà abbastanza dettagliato e abbondante, specialmente se si utilizza una libreria di promise che supporta altre funzionalità come la cancellazione o il passaggio di messaggi. O forse lo farà in futuro, o vuoi scambiare la tua biblioteca con una migliore? Non vorrai riscrivere il tuo codice per quello.

I metodi delle librerie ( then ) non supportano solo in modo nativo tutte le funzionalità, ma potrebbero anche avere alcune ottimizzazioni in atto. Usarli probabilmente renderà il tuo codice più veloce, o almeno permetterà di essere ottimizzato dalle future revisioni della libreria.

Come lo evito?

Pertanto, ogni volta che ti ritrovi a creare manualmente Promise o Deferred e sono già presenti promesse esistenti, controlla prima l’API della libreria . L’antipattern Deferred è spesso applicato da persone che vedono le promesse [solo] come un modello osservatore – ma le promesse sono più che callback : dovrebbero essere componibili. Ogni libreria decente ha molte funzioni facili da usare per la composizione delle promesse in ogni modo pensabile, avendo cura di tutte le cose di basso livello che non vuoi trattare.

Se hai trovato la necessità di comporre alcune promesse in un modo nuovo che non è supportato da una funzione helper esistente, scrivere la tua funzione con Deferred inevitabili dovrebbe essere la tua ultima opzione. Prendi in considerazione il passaggio a una libreria più funzionale e / o un bug alla tua libreria corrente. Il suo manutentore dovrebbe essere in grado di ricavare la composizione dalle funzioni esistenti, implementare una nuova funzione di supporto per te e / o aiutare a identificare i casi limite che devono essere gestiti.