Posizionamento della cattura PRIMA e DOPO poi


103

Ho difficoltà a capire la differenza tra mettere .catchPRIMA e DOPO poi in una promessa annidata.

Alternativa 1:

test1Async(10).then((res) => {
  return test2Async(22)
    .then((res) => {
      return test3Async(100);
    }).catch((err) => {
      throw "ERROR AFTER THEN";
    });
}).then((res) => {
  console.log(res);
}).catch((err) => {
  console.log(err);
});

Alternativa 2:

test1Async(10).then((res) => {
   return test2Async(22)
     .catch((err) => {
        throw "ERROR BEFORE THEN";
      })
      .then((res) => {
        return test3Async(100);
      });
  }).then((res) => {
    console.log(res);
  }).catch((err) => {
    console.log(err);
  });

Il comportamento di ciascuna funzione è il seguente, <0test1 fallisce se numero è test2 fallisce se numero è > 10e test3 fallisce se numero non lo è 100. In questo caso test2 sta solo fallendo.

Ho provato a eseguire e fare fallire test2Async, sia PRIMA che DOPO quindi si comporta allo stesso modo e questo non sta eseguendo test3Async. Qualcuno può spiegarmi la differenza principale nel posizionare le catture in posti diversi?

In ogni funzione io console.log('Running test X')per controllare se viene eseguita.

Questa domanda sorge a causa del thread precedente che ho pubblicato Come trasformare la richiamata annidata in promessa? . Immagino sia un problema diverso e valga la pena postare un altro argomento.


sia .then che .catch possono alterare la promessa ... quindi non sono sicuro da dove provenga il malinteso. Se metti catch prima di .then, catturerà i rifiuti che si sono verificati prima di .then e .then eseguirà i callback eseguiti / falliti in base a ciò che accade all'interno del .catch, e viceversa quando li scambi.
Kevin B

Scusa se la mia domanda non era chiara. Ma in questo caso, come ho detto, entrambi i casi si comportano allo stesso modo, quindi non posso vedere la differenza. Puoi dirmi quando mettiamo il fermo PRIMA e quando abbiamo deciso di metterlo DOPO allora? metterlo dopo sembra davvero intuitivo e comune. Non sono sicuro del motivo per cui a volte lo mettiamo prima di allora
Zanko

Se eseguono lo stesso, è semplicemente perché ciò che ciascuno fa non altera il risultato in questo caso specifico. Una piccola modifica a entrambi potrebbe alterare il risultato.
Kevin B

Cosa intendi per "alterare il risultato". Scusa, sono davvero confuso haha
Zanko

Ad esempio, se invece di lanciare un errore non hai fatto nulla, la promessa passerebbe dall'essere rifiutata alla risoluzione. Ciò ovviamente altererebbe il risultato, perché la promessa è ora una promessa risolta piuttosto che rifiutata. (a meno che, ovviamente, non fosse già stato risolto, nel qual caso il problema non sarebbe comunque andato a buon fine)
Kevin B

Risposte:


237

Quindi, in pratica stai chiedendo qual è la differenza tra questi due (dov'è puna promessa creata da un codice precedente):

return p.then(...).catch(...);

e

return p.catch(...).then(...);

Ci sono differenze quando p risolve o rifiuta, ma se tali differenze contano o meno dipende da ciò che fa il codice all'interno dei gestori .then()o .catch().

Cosa succede quando si prisolve:

Nel primo schema, quando si prisolve, .then()viene chiamato il gestore. Se quel .then()gestore restituisce un valore o un'altra promessa che alla fine si risolve, il .catch()gestore viene ignorato. Ma, se il .then()gestore lancia o restituisce una promessa che alla fine rifiuta, allora il .catch()gestore eseguirà sia un rifiuto nella promessa originale p, ma anche un errore che si verifica nel .then()gestore.

Nel secondo schema, quando si prisolve, .then()viene chiamato il gestore. Se quel .then()gestore lancia o restituisce una promessa che alla fine rifiuta, allora il .catch()gestore non può prenderla perché è prima di essa nella catena.

Quindi, questa è la differenza n. 1. Se il .catch()gestore è DOPO, può anche rilevare errori all'interno del .then()gestore.

Cosa succede quando prifiuta:

Ora, nel primo schema, se la promessa viene prifiutata, il .then()gestore viene ignorato e il .catch()gestore verrà chiamato come ci si aspetterebbe. Quello che fai nel .catch()gestore determina cosa viene restituito come risultato finale. Se restituisci semplicemente un valore dal .catch()gestore o restituisci una promessa che alla fine si risolve, la catena della promessa passa allo stato risolto perché hai "gestito" l'errore e hai restituito normalmente. Se lanci o restituisci una promessa rifiutata nel .catch()conduttore, la promessa restituita rimane rifiutata.

Nel secondo schema, se la promessa viene prifiutata, .catch()viene chiamato il gestore. Se restituisci un valore normale o una promessa che alla fine si risolve dal .catch()gestore (quindi "gestendo" l'errore), la catena della promessa passa allo stato risolto e il .then()gestore dopo .catch()verrà chiamato.

Quindi questa è la differenza n. 2. Se il .catch()gestore è PRIMA, può gestire l'errore e consentire al .then()gestore di essere comunque chiamato.

Quando usarlo:

Usa il primo schema se vuoi un solo .catch()gestore in grado di rilevare errori nella promessa originale po nel .then()gestore e un rifiuto da pdovrebbe saltare il .then()gestore.

Usa il secondo schema se vuoi essere in grado di rilevare gli errori nella promessa originale pe forse (a seconda delle condizioni), consentire alla catena della promessa di continuare come risolta, eseguendo così il .then()gestore.

L'altra opzione

C'è un'altra opzione per utilizzare entrambi i callback a cui puoi passare .then()come in:

 p.then(fn1, fn2)

Ciò garantisce che solo uno di fn1o fn2verrà mai chiamato. Se si prisolve, fn1verrà chiamato. Se prifiuta, fn2verrà chiamato. Nessun cambiamento di risultato fn1potrà mai fn2farti chiamare o viceversa. Quindi, se vuoi essere assolutamente sicuro che solo uno dei tuoi due gestori venga chiamato indipendentemente da ciò che accade negli stessi gestori, puoi usare p.then(fn1, fn2).


17
La domanda riguarda specificamente l'ordine di .then()e .catch(), a cui rispondi. Inoltre si danno alcuni suggerimenti su quando usare quale ordine, dove penso sia opportuno menzionare una terza opzione, vale a dire passare sia il gestore di successo che quello di errore a .then () . In tal caso verrà chiamato al massimo un gestore.
ArneHugo

7
@ArneHugo - Buon suggerimento. Ho aggiunto.
jfriend00

Quindi, durante Promise Chaining possiamo scrivere .quindi .catch .catch .quindi tipo di scenari?
Kapil Raghuwanshi

@KapilRaghuwanshi, sì, puoi usarlo per passare un valore predefinito in caso di errore. cioè Promise.reject(new Error("F")).then(x => x).catch(e => {console.log(e); return [1]}).then(console.log)e Promise.resolve([2]).then(x => x).catch(e => [1]).then(console.log)
CervEd

1
@DmitryShvedov - Come immaginavo, questo è sbagliato .then(this.setState({isModalOpen: false})). Non stai passando un riferimento a una funzione in .then()modo che il codice nelle parentesi venga eseguito immediatamente (prima che la promessa si risolva). Dovrebbe essere .then(() => this.setState({isModalOpen: false})).
jfriend00

31

La risposta di jfriend00 è eccellente, ma ho pensato che sarebbe stata una buona idea aggiungere l'analogo codice sincrono.

return p.then(...).catch(...);

è simile al sincrono:

try {
  iMightThrow() // like `p`
  then()
} catch (err) {
  handleCatch()
}

Se iMightThrow()non lancia, then()verrà chiamato. Se lancia (o se then()lancia esso stesso), handleCatch()verrà chiamato. Notare come il catchblocco non ha alcun controllo sul fatto che thenvenga chiamato o meno .

D'altro canto,

return p.catch(...).then(...);

è simile al sincrono:

try {
  iMightThrow()
} catch (err) {
  handleCatch()
}

then()

In questo caso, se iMightThrow()non viene lanciato, then()verrà eseguito. Se lancia, spetta a handleCatch()decidere se then()chiamare, perché se viene handleCatch()rilanciato, then()non verrà chiamato, poiché l'eccezione verrà lanciata immediatamente al chiamante. Se handleCatch()può gestire con garbo il problema, then()verrà chiamato.


questa è una buona spiegazione ma potresti avvolgere l'orfano then()in unfinally{...}
tyskr

2
@ 82Tuskers, sei sicuro? Se metto then()in finally{...}, non sarebbe errato essere chiamato anche se handleCatch()getta? Tieni presente che il mio obiettivo era mostrare un codice sincrono analogo, non suggerire modi diversi di gestire le eccezioni
akivajgordon

Quindi, se vogliamo gestire tutti i casi ma ancora chain .then () sarebbe meglio usare .then (fai qualcosa) .catch (log err e update state) .then (fai un'altra cosa) .catch (log err) dove tentiamo di catturare in ogni punto ma continuiamo anche a eseguire gli stmnts?
anna
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.