2014-05-22 9 views
330

stavo scrivendo il codice che fa qualcosa che assomiglia a:Qual è la promessa esplicita di costruzione antipattern e come posso evitarlo?

Utilizzando la Promise costruttore:

function getStuffDone(param) { 
    return new Promise(function(resolve, reject) { 
     myPromiseFn(param+1) 
      .then(function(val) { resolve(val); }) // or .then(resolve) 
      .catch(function(err) { reject(err); }); // or .catch(reject) 
    }); 
} 

Utilizzando Q o differita:

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

Qualcuno mi ha detto questo è chiamato il "differito antipattern "o" Promise costruttore antipattern "rispettivamente, wh non va bene per questo codice e perché questo è chiamato antipattern?

+0

Posso confermare che la rimozione di questo è (nel contesto di destra, non a sinistra, esempio) rimuovere il wrapper di funzione 'getStuffDone' e utilizzare solo il letterale Promesso? –

+0

o il blocco 'catch' nel wrapper' getStuffDone' è l'antipattern? –

+1

Per qualche secondo, sono rimasto stupito dal fatto che @BenjaminGruenbaum avrebbe * fatto * una domanda sulle promesse invece di * rispondere * uno :) – max

risposta

238

Il deferred antipattern (now explicit-construction anti-pattern) coniato da Esailija è un popolo anti-modello comune che è nuovo alle promesse di fare, l'ho fatto da solo 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 con .then e puoi restituire le promesse direttamente. Il codice in getStuffDone può essere riscritta come:

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

promesse sono tutti di fare un codice asincrono più leggibile e si comportano come il codice sincrono senza nascondere questo fatto. 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 solo oggetti posticipati quando si è converting an API to promises e non è possibile farlo automaticamente o quando si scrivono funzioni di aggregazione che sono più facili espresse in questo modo.

Citando Esailija:

Questa è l'anti-modello 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.

+9

AKA "The Forgotten Promise" [qui] (http://taoofcode.net/promise-anti- patterns /) –

+0

@BenjaminGruenbaum: Sono fiducioso nel mio uso dei differiti per questo, quindi non c'è bisogno di una nuova domanda. Ho solo pensato che fosse un caso d'uso che ti mancava dalla tua risposta. Quello che sto facendo sembra più l'opposto dell'aggregazione, vero? – mhelvens

+1

@mhelvens Se si divide manualmente un'API non callback in un'API di promessa che si adatta alla parte "conversione di una callback API in promesse". L'antipattern riguarda il confezionamento di una promessa in un'altra promessa senza una buona ragione, non si sta avviando una promessa per iniziare, quindi non si applica qui. –

83

Cosa c'è di sbagliato in questo?

Ma lo schema funziona!

Fortunato. Sfortunatamente, probabilmente no, dato che probabilmente hai dimenticato un caso limite. In più della metà degli eventi che ho visto, l'autore ha dimenticato di prendersi cura del gestore degli errori:

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

Se l'altra promessa viene rifiutato, questo accadrà inosservato, invece di essere propagato per la nuova promessa (dove verrebbe gestito) - e la nuova promessa rimane sospesa per sempre, che può indurre fughe.

La stessa cosa accade nel caso in cui il codice di richiamata causi un errore - ad es. quando result non ha un property e viene generata un'eccezione. Ciò andrebbe non gestito e lascerà la nuova promessa irrisolta.

Al contrario, utilizzando .then() non prendere automaticamente la cura di entrambi questi scenari, e respinge la nuova promessa quando si verifica un errore:

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

L'antipattern differita non è solo ingombrante, ma anche soggetto ad errori . L'utilizzo di .then() per il concatenamento è molto più sicuro.

Ma ho gestito tutto!

Davvero? Buona. Tuttavia, questo sarà abbastanza dettagliato e abbondante, specialmente se si utilizza una libreria di promessa 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 posso evitarlo?

Così ogni volta che vi trovate a creare manualmente un Promise o Deferred e già sono coinvolti promesse esistenti, controllo la libreria API prima. L'antipattern Deferred è spesso applicato da persone che vedono le promesse [solo] come un modello osservatore - ma promises are more than callbacks: 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 di supporto esistente, la scrittura della propria 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.

+0

Esistono esempi, oltre a una funzione che include 'setTimeout', dove il costruttore può essere usato ma non può essere considerato" Promise costruttore anitpattern "? – guest271314

+1

@ guest271314: tutto asincrono che non restituisce una promessa. Anche se abbastanza spesso ottieni risultati migliori con gli aiuti dedicati alla promozione delle biblioteche. E assicurati di promettere sempre al livello più basso, quindi non è "* una funzione che include" setTimeout' * ", ma" * la funzione 'setTimeout' stesso *". – Bergi

+0

_ "E assicurati di promettere sempre al livello più basso, quindi non è" una funzione che include "setTimeout'", ma "la funzione' setTimeout' stesso "" _ Può descrivere, collegare alle differenze, tra i due? – guest271314