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.
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. –
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 :) –
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