Gestori di errori all'interno di Promise.all

1

Sto guardando questo pezzo di codice e mentre funziona, mi sembra sbagliato. Mi chiedo se sto guardando un anti-pattern?

Il codice inizia con due promesse, getAccounts e getTransactions . Su una delle promesse c'è un blocco catch che gestisce e errore ma restituisce una promessa risolta con un oggetto che sembra amichevole .

let accounts = getAccounts()
                   .catch(error => Promise.resolve({errorCode: M011}));

let transactions = getTransactions();

Questi vengono quindi utilizzati in Promise.all .

Promise.all([accounts, transactions])
       .then(results => createStatement(results));

Questo mi sembra sbagliato perché su un errore di getAccounts ho pensato che Promise.all dovrebbe fallire velocemente . Tuttavia d'altro canto il fallimento di getAccounts viene gestito il prima possibile che, sicuramente, è una buona cosa? L'unica cosa a riguardo è che non ritorna con un altro Promise.reject

La logica è che in questo modo Promise.all non soffoca. Ovviamente un'altra domanda è: perché questo pezzo di codice usa Promise.all in primo luogo? Che non conosco, codice legacy.

    
posta piratemurray 10.04.2016 - 23:29
fonte

1 risposta

1

Questo non è un anti-modello. Questo è un codice olfattivo , il che vuol dire che questo è il tipo di cosa che dovresti evitare quando è fattibile, ma potrebbero esserci casi legittimi in cui nient'altro svolge il lavoro.

I thought the Promise.all should fail fast.

Yup. Questo è esattamente il motivo per cui il comportamento predefinito di Promise.all è fallire rapidamente.

The rationale given was that this way the Promise.all doesn't choke.

Suppongo che questo significhi che sei in una situazione in cui anche se alcune delle promesse si rifiutano, puoi comunque fare qualcosa di utile con le altre promesse che si sono risolte con successo. Questa non è sicuramente una situazione insolita.

Of course another question entirely is why is this piece of code using Promise.all in the first place?

Presumibilmente perché quella cosa utile che puoi fare con tutte le promesse fatte, tuttavia, può essere risolta solo dopo che sono state risolte tutte.

Supponiamo per un momento che le promesse rappresentino le transazioni bancarie e che l'interfaccia utente visualizzerà il testo verde o rosso in base al fatto che la transazione abbia avuto esito positivo.

Se solo alcune transazioni hanno esito positivo e altre falliscono, vorresti sicuramente mostrare quelle di successo in verde e quelle fallite in rosso, piuttosto che "soffocarti" e accontentarti di un singolo frustrantemente disinformativo "una o più delle tue transazioni potrebbero hanno fallito "messaggio. In quel contesto, non riesco a immaginare nessuno che si opponga al codice in questo modo:

let resultRowPromise = executeTransactions(desiredTransactions)
           .then(result => Promise.resolve({ color: "green", message: "Success =D" }))
           .catch(error => Promise.resolve({ color: "red",   message: "Fail ;_;" }));

che potremmo "ricominciare" con un Promise.all ad un certo punto.

Questo è un caso in cui ha senso che la promessa di fondo venga respinta, è logico attendere che una serie di tali promesse finisca, e ha senso gestire i fallimenti secondo le promesse invece di trattare tutti i fallimenti come un fallimento per l'intero lotto.

Ma se qualcuno di questi non è il caso dell'esempio getAccounts (), allora dovresti prendere in considerazione alternative come fare in modo che la promessa sottostante non venga rifiutata in primo luogo, reagendo a ciascuna risoluzione promessa individualmente invece di aspettare una Promessa. tutto, o semplicemente lasciare che Promise.all fallisca velocemente se nel tuo caso è del tutto normale ignorare quelli di successo.

In effetti, anche l'esempio che ho appena dato non soddisfa necessariamente quegli standard. È probabile che tu possa anche gestire singolarmente ciascuna promessa di transazione, quindi l'utente vede "Transazione 1 riuscita!" e poi qualche secondo dopo "Transaction 2 failed!". Potresti quindi fare un Promise.all sulle promesse per il rendering di questi messaggi (che dovrebbe avere sempre successo a meno che non ci sia un errore di rendering) in modo da poter inserire un messaggio aggiuntivo come "Tutte le transazioni riuscite!" o "3/10 transazioni fallite!" in cima.

Quando si pensa a come le promesse propagano risultati ed errori, trovo spesso utile considerare il codice equivalente "normale", "sincrono" o "imperativo" non promettente che restituisce valori e genera / rileva eccezioni. Per il tuo esempio, probabilmente sarebbe:

var results = [];
for(let i = 0; i < accountIds.length; ++i) {
    try {
        var account = getAccount(accountIds[i]);
        results.push(account);
    } catch(e) {
        results.push({ errorCode: M011 });
    }
}

Tutto ciò che ho detto sopra si applica anche a questo codice, sebbene vederlo in questo modo potrebbe rendere più ovvio il motivo per cui si applica. Riassumendo: questo potrebbe essere legittimo, ma ha sicuramente bisogno di giustificazione, e vale sicuramente la pena di riflettere se questa giustificazione regge o se ci sono alternative più pulite.

    
risposta data 11.04.2016 - 00:17
fonte

Leggi altre domande sui tag