2015-10-20 1 views
5

Sto usando Oboe.js di analizzare un file di grandi dimensioni davvero JSONnon gestita promessa rigetto nei promesse asincroni

const promises = []; 
oboe('http://domain/my-file.js') 
    .node('items.*', item => { 
    // parseItem() returns a rejected Promise because of invalid JSON items 
    promises.push(parseItem(item)); 
    }) 
    .done(() => { 
    Promise.all(promises).then(() => { 
     doSomething(); 
    }); 
    }) 

Ma la mia console Browser si allaga con Uncaught (in promise). Lo stesso accade se si scrive una promessa in un setTimeout() come

const promises = []; 
setTimeout(() => { 
    promises.push(Promise.reject()); 
}, 500); 
// some time in the future 
Promise.all(promises); 

cosa veramente strana: i browser moderni comportano in modo diverso. In Firefox Developer Edition tutto funziona senza i messaggi di errore e in Chrome sono inondato di Uncaught (in promise). In Chrome ricevi immediatamente il messaggio se scrivi Promise.reject(); senza un fermo. In Firefox e Safari non succede niente.

Quindi qual è la soluzione per questo? Ignorando il messaggio? Voglio dire se questo comportamento è davvero nelle specifiche ufficiali di promessa, quindi le promesse in codice asincrono non hanno molto senso per me.

+1

Attualmente ci sono due campi di persone per quanto riguarda le promesse di es6. Un campo (gli sviluppatori di Chrome inclusi) considerano il modo in cui si utilizzano le promesse come un uso improprio. Il comportamento che stai vivendo non è nelle specifiche per le promesse nonostante ci siano diverse proposte perché l'altro campo continua a ribattere dicendo che casi come il tuo sono rilevanti. Sfortunatamente, alcuni dei browser hanno aggiunto promesse di rifiuto non gestite, nonostante la mancanza di specifiche e nonostante il respingimento. –

+0

@MicahZoltu per essere onesti, i campi sono divisi anche tra gli sviluppatori di Chrome. –

+0

@MicahZoltu grazie per i chiarimenti. Al momento è davvero confuso quale implementazione sarà quella giusta. – LongFlick

risposta

2

Il tuo problema di Oboe si basa sul fatto che i flussi di Oboe sono JSON, quindi non sappiamo mai in anticipo quante promesse ci sono, quindi non possiamo attribuire la responsabilità allo Promise.all in anticipo. Possiamo "promettere" a Oboe di essere in grado di restituire promesse nei suoi metodi.

In genere, utilizzerei RxJS per creare automaticamente un flusso dall'emettitore di eventi ei metodi di RxJS possono già restituire le promesse e quindi aggregarle. Tuttavia - dal momento che io non voglio una libreria di terze parti qui, e ha meno valore di insegnamento - cerchiamo di attuare da soli:

function addMap(oboe) { 
    oboe.map = function(selector, mapper){ 
    var promises = []; 
    return new Promise(function(resolve, reject){ // create a new promise 
     oboe.node(selector, function(match){ 
     var result = mapper(match); // get result 
     // signal that we're handling the rejection to make sure it's not handled. 
     result.catch(function(){}); 
     promises.push(result); 
     }); 
     oboe.fail(reject); 
     oboe.done(function(){ resolve(promises); }); 
    }); 
    }; 
} 

Che sarebbe farci fare:

var o = oboe("foo"); 
addMap(o); 
o.map("items.*", item => downloadItem(item)).then(result => { 
    // handle result here 
}); 

Il tuo problema setTimeout è molto artificioso. La stragrande maggioranza delle persone non scrive codice simile a questo in pratica - in pratica l'aggiunta di un gestore di errori in modo asincrono è un caso di utilizzo piuttosto raro quando non funziona contro un'API che ti costringe a farlo (come nell'esempio Oboe.js).

cosa veramente strana: i browser moderni si comportano in modo diverso

Questo perché Firefox utilizza GC per la rilevazione non gestita respingimenti e Chrome un timer. È il dettaglio dell'implementazione - l'unica garanzia che si avrà è che gli errori non verranno registrati se collegati all'interno di una microtask (in modo sincrono o in uno then che viene eseguito nello stesso turno).

+0

Grazie per l'esempio. Puoi trasferirlo a qualsiasi API basata sul callback come in Node.js o in una semplice richiesta AJAX: se si avvolge un'API callback con una promessa e questa richiamata viene chiamata in futuro e si deve rifiutare la promessa, si avere lo stesso problema Nel mio caso, avvolgo Oboe con una promessa e chiamo una seconda API basata sul callback e lo avvolgo in una promessa. La mia seconda promessa viene respinta e poi ho ricevuto l'errore sopra.Allora, qual è la soluzione per questo? Sfortunatamente sono molto limitato nel sistema di commenti StackOverflow, quindi non posso scrivere troppo qui. – LongFlick

+0

Oppure: Non posso usare promesse che sono rifiutate in futuro e sono gestite con Promise.all(). Il problema principale qui è che non ho una presa diretta() sulla mia promessa, ma quindi nella mia chiamata a Promise.all(). Se faccio la presa() sulla mia promessa interiore funziona, ma non voglio quello. Voglio prenderlo in Promise.all(). Credo di non averlo capito. Quindi, qual è la soluzione per le API in cui ho promesse che chiama API che sono anche promesse ma che non hanno un gestore catch(). Dovrebbero tutti avere gestori di catch()? Ho pensato che Promise.all(). Catch() era esattamente quello che sto cercando per – LongFlick

+0

@LongFlick 'var p = Promise.reject(); p.catch (() => {});/* nota che non abbiamo cambiato p, è la stessa promessa, abbiamo appena aggiunto un gestore catch * /; p.then (...);/* non più report di rifiuto non gestito, abbiamo optato esplicitamente */' –