2015-02-27 18 views
8

Breve storia:Come rifiutare (e utilizzare correttamente) le promesse?

  • parlando Promesse/A +, qual è il modo corretto di rifiutare una promessa - gettando un errore? Ma se mi manca il catch - la mia intera app esploderà!

  • Come utilizzare promisify e quali sono i vantaggi (forse è necessario leggere la versione più lunga)?

  • È .then(success, fail) davvero un anti-pattern e devo sempre usare .then(success).catch(error)?

Longer versione, descritto come problema di vita reale (mi piacerebbe che qualcuno a leggere):

Ho una libreria che utilizza Bluebird (una libreria implementazione + promessa), per recuperare i dati dal database (parlando circa Sequelize). Ogni query restituisce un risultato, ma a volte è vuoto (provato a selezionare qualcosa, ma non ce n'era). La promessa va alla funzione result, perché non c'è alcun motivo per un errore (non avere risultati non è un errore). Esempio:

Entity.find(1).then(function(result) { 
    // always ending here, despite result is {} 
}).catch(function(error) { 
    // never ends here 
}); 

voglio avvolgere questo e verificare se il risultato è vuoto (non può controllare questo ovunque nel mio codice). Ho fatto questo:

function findFirst() { 
    return Entity.find(1).then(function(result) { 
    if (result) { // add proper checks if needed 
     return result; // will invoke the success function 
    } else { 
     // I WANT TO INVOKE THE ERROR, HOW?! :) 
    } 
    }).catch(function(error) { 
    // never ends here 
    }); 
} 

findFirst().then(function(result) { 
    // I HAVE a result 
}).catch(function(error) { 
    // ERROR function - there's either sql error OR there is no result! 
}); 

Se sei ancora con me, spero che capirai cosa succede. Voglio in qualche modo attivare la funzione di errore (dove "funzione ERRORE" è). Il fatto è che non so come. Ho provato queste cose:

this.reject('reason'); // doesn't work, this is not a promise, Sequelize related 
return new Error('reason'); // calls success function, with error argument 
return false; // calls success function with false argument 
throw new Error('reason'); // works, but if .catch is missing => BLOW! 

Come si può vedere dai miei commenti (e per ogni specifica), gettando un errore funziona bene. Ma c'è un grande ma - se mi manca la dichiarazione .catch, la mia intera app esplode.

Perché non lo voglio? Diciamo che voglio incrementare un contatore nel mio database. Non mi interessa il risultato: faccio solo una richiesta HTTP .. Quindi posso chiamare incrementInDB(), che ha la capacità di restituire i risultati (anche per motivi di test), quindi c'è throw new Error se non è riuscito. Ma dal momento che non mi interessa la risposta, a volte non aggiungo l'istruzione .catch, giusto? Ma ora - se non lo faccio (apposta o per colpa) - finisco con l'app del nodo verso il basso.

Non trovo questo molto bello. C'è qualche altro modo migliore per risolverlo, o devo solo occuparmene?

Un mio amico mi ha aiutato e ho usato una nuova promessa per sistemare le cose, in questo modo:

function findFirst() { 
    var deferred = new Promise.pending(); // doesnt' matter if it's Bluebird or Q, just defer 
    Entity.find(1).then(function(result) { 
    if (result) { // add proper checks if needed 
     deferred.resolve(result); 
    } else { 
     deferred.reject('no result'); 
    } 
    }).catch(function(error) { 
    deferred.reject('mysql error'); 
); 

    return deferred.promise; // return a promise, no matter of framework 
} 

funziona come un fascino! Ma Mi sono occupato di questo: Promise Anti Patterns - articolo wiki scritto da Petka Antonov, creatore di Bluebird (implementazione A +). Si dice esplicitamente che questo è sbagliato.

Quindi la mia seconda domanda è - è così? Se sì, perché? E qual è il modo migliore?

Grazie mille per la lettura di questo, spero che qualcuno sarà trascorso il tempo di rispondere per me :) Vorrei aggiungere che io non volevo dipendere troppo sui quadri, in modo Sequelize e Bluebird sono solo cose che ho finito lavorare con. Il mio problema è con le Promesse come globale, non con questi particolari framework.

+1

Per il 'Ma, c'è un grande ma - se mi manca l'istruzione .catch, tutta la mia app esploderà. Non è questo che sono le eccezioni? Dovresti sempre gestire l'eccezione, anche se non ti interessa l'errore che dovresti chiarire nel codice aggiungendo un '.catch' con un commento a cui non ti importa. Immagina di rivedere il tuo codice in un secondo momento e di riconoscere che esiste un'eccezione non gestita, ma non ti ricordi perché non è gestita, quindi passerai molto tempo a determinarne il motivo. –

+0

Riguarda la produttività, il lavoro con più sviluppatori e non alla fine, l'affidabilità. È più facile per qualsiasi sviluppatore scrivere semplicemente "incrementInDB();" piuttosto che navigare sul codice, leggi che questa è una promessa che genera un errore, quindi devi ** prenderlo.Come ho detto - non voglio eccezioni - non riesco proprio a capire un altro modo per farlo senza far esplodere la mia intera app :) –

+1

Penso che il commento di t.niese dovrebbe essere una risposta. Sì, potrebbe essere * più facile * per qualsiasi sviluppatore scrivere una riga di codice piuttosto che scrivere * codice * corretto, ma non è una scusa. Troppo spesso vedo codice da sviluppatori inesperti in cui le eccezioni generate da alcune API vengono silenziosamente ingerite e convertite in restituzioni nulle ecc. Alcuni sviluppatori sembrano avere qualche tipo di paura fondamentale delle eccezioni. Con l'uso corretto di catch() e done() non vedo alcun problema. Inoltre, potresti sempre scrivere il tuo gestore di eccezioni non catturato, se necessario? – JHH

risposta

5

Parlare di Promesse/A +, qual è il modo corretto di rifiutare una promessa - lanciare un errore? Ma se mi manca il trucco, la mia intera app esploderà!

E se si utilizza i codici di ritorno per segnalare gli errori, invece di eccezioni, manca il controllo causerà bug sottili e comportamento irregolare. Dimenticando di gestire gli errori è un bug, ma avere la vostra applicazione esplodere in un caso del genere spiacevole è more often better che lasciare procedi in stato danneggiato.

Il debug del codice di errore dimenticato verifica che l'applicazione si comporti in modo strano in un punto non correlato alla fonte di errore è facilmente più di un ordine di grandezza più costoso del debug di un blocco dimenticato perché si ha l'errore messaggio, origine dell'errore e traccia dello stack. Quindi, se si desidera produttività nello scenario tipico di sviluppo di applicazioni, le eccezioni vincono con un margine piuttosto ampio.

.then (buon fine, non riescono)

Questo di solito è il segnale che si sta utilizzando promesse callback come glorificati in cui i callback sono semplicemente passati separatamente. Questo è ovviamente un anti-modello in quanto non otterrai alcun beneficio dall'usare le promesse in questo modo. Se questo non è il caso, anche in questo caso si sta solo facendo il vostro codice di un po 'meno leggibile rispetto all'utilizzo di .catch(), simile a come method(true, true, false) è molto meno leggibile di method({option1: true, option2: true, option3: false}).

Come usare promisify e quali sono i vantaggi (forse avrete bisogno di leggere la versione più lunga)?

La maggior parte dei moduli espone un'interfaccia di callback, promette di trasformare automaticamente un'interfaccia di callback in un'interfaccia promettente. Se stai scrivendo manualmente questo codice (usando differito o new Promise), stai perdendo tempo.

Quindi la mia seconda domanda è - è così? Se sì, perché? E qual è il modo migliore?

Il modo migliore è quello di catena la promessa:

function findFirst() { 
    return Entity.find(1).tap(function(result) { 
    if (!result) throw new Error("no result"); 
    }); 
} 

E 'meglio perché è più semplice, più breve, più leggibile e molto meno soggetto a errori modo di fare esattamente la stessa cosa.

+0

Grazie per la risposta breve. Devo solo notare l'articolo che hai menzionato (codici di errore vs eccezioni) - se lo leggi attentamente, vedrai che le eccezioni hanno molti svantaggi e che "dovrebbero essere abbracciati da tutti i programmatori pigri che scrivono codice di bassa qualità come me ". Forse è solo una questione di opinione. Grazie per la risposta però! –

+1

@AndreyPopov Penso che dovresti scendere [le basi] (http://codebetter.com/karlseguin/2006/04/05/understanding-and-using-exceptions/) a meno che non lavori alla NASA, nel qual caso dovresti usare codici di errore – Esailija

8

chiedere prego soltanto una sola domanda per ogni post :-)

È .then(success, fail) davvero un anti-modello e devo sempre usare .then(success).catch(error)?

No.They just do different things, ma una volta che sai che puoi scegliere quello appropriato.

Come utilizzare promisify e quali sono i benefici di esso?

credo che la Bluebird Promisification docs spiegare questo abbastanza bene - è usato per convert a callback api a uno che ritorna promesse.

Parlando di Promesse/A +, qual è il modo corretto di rifiutare una promessa - gettando un errore?

Sì, il lancio di un errore è assolutamente soddisfacente. All'interno di una richiamata then, è possibile lanciare e verrà catturato automaticamente, con conseguente rifiuto della promessa del risultato.

È inoltre possibile utilizzare return Promise.reject (new Error(…));; entrambi avranno assolutamente lo stesso effetto.

Un mio amico mi ha aiutato e ho usato una nuova promessa per sistemare le cose, come questo: [...]

n You really shouldn't use that. Basta usare then e lanciare o restituire una promessa respinta in là.

Ma se mi manca la dichiarazione di cattura, tutta la mia app esploderà!

No, non lo farà. Si noti che il metodo .catch() non è una dichiarazione try catch - l'errore verrà già rilevato dove è stata richiamata la richiamata then. Viene quindi passato al callback catch come argomento, la chiamata .catch() non è necessaria per acquisire le eccezioni.

E quando si perde .catch(), la tua app non esploderà. Tutto quello che succede è che hai una promessa respinta in giro, il resto della tua app non sarà influenzato da questo. Otterrai uno unhandled rejection event, che può essere dealt with globally.

Ovviamente, che non dovrebbe essere; ogni promessa catena (non ogni promessa esempio) dovrebbe essere conclusa con un .catch() che gestisce gli errori in modo appropriato. Ma sicuramente non hai bisogno di .catch() in ogni funzione di supporto, quando restituisce una promessa da qualche altra parte, quindi gli errori saranno solitamente gestiti dal chiamante.

Problemi correlati