2015-10-20 12 views
5

Używam Oboe.js do analizowania naprawdę bardzo duży plik JSONUnhandled odrzucenie obietnica w asynchronicznych obietnic

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(); 
    }); 
    }) 

Ale moja konsola Przeglądarka zostanie zalany Uncaught (in promise). To samo dzieje się, jeśli piszesz obietnicę w setTimeout() jak

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

Co naprawdę dziwne: Nowoczesne przeglądarki zachowują się inaczej. W Firefox Developer Edition wszystko działa bez komunikatów o błędach, a w Chrome jestem zalewany Uncaught (in promise). W Chrome natychmiast otrzymasz wiadomość, jeśli napiszesz bez żadnego haczyka Promise.reject();. W Firefoksie i Safari nic się nie dzieje.

Jakie jest rozwiązanie tego problemu? Ignorujesz wiadomość? Mam na myśli, jeśli to zachowanie jest naprawdę w oficjalnej specyfikacji obietnicy, to obietnice w asynchronicznym kodzie nie mają dla mnie większego sensu.

+1

Obecnie istnieją dwa obozy ludzi pod względem obietnic es6. Jeden obóz (włączając w to programistów Chrome) rozważ, w jaki sposób używasz obietnic, aby być niewłaściwym użytkiem. Zachowanie, którego doświadczasz, nie jest zgodne z obietnicą obietnic, mimo że jest kilka propozycji, ponieważ drugi obóz ponownie naciska, że ​​przypadki użycia, takie jak twoje, są istotne. Niestety, niektóre przeglądarki dodały nieobsługiwane obietnice odrzucania zgłoszeń pomimo braku specyfikacji i pomimo tego, że cofają się przeciwko niemu. –

+0

@MicahZoltu uczciwie, obozy są podzielone nawet między chromowanymi programistami. –

+0

@MicahZoltu dziękuję za wyjaśnienia. W tej chwili bardzo mylące jest to, która implementacja będzie właściwa. – LongFlick

Odpowiedz

2

Twoje wydanie oboju opiera się na fakcie, że Oboe kieruje JSON, więc nigdy nie wiemy z góry, ile jest obietnic, więc nie możemy z góry przypisać odpowiedzialności do Promise.all. Możemy "obiecywać" obój, aby móc zwracać obietnice w swoich metodach.

Zazwyczaj używałbym RxJS do automatycznego tworzenia strumienia z emitera zdarzeń - a metody RxJS mogą już zwracać obietnice, a następnie je agregować. Jednak - ponieważ nie chcę bibliotekę strony trzeciej tutaj i ma mniejszą wartość nauczania - niech wdrożyć go sami:

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); }); 
    }); 
    }; 
} 

Które czyńmy:

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

Twój setTimeout kwestią jest bardzo wymyślne. Zdecydowana większość ludzi nie pisze kodu, który wygląda tak w praktyce - w praktyce asynchroniczne dodawanie obsługi błędów jest dość rzadkim przypadkiem, gdy nie działa się przeciwko API, które zmusza do tego (np. Przykład Oboe.js).

Co naprawdę dziwne: Nowoczesne przeglądarki zachowują się inaczej

To dlatego Firefox używa do wykrywania GC nieobsługiwany odrzucenia i Chrome czasomierza. Jest to szczegół implementacji - jedyną gwarancją będzie to, że błędy nie będą rejestrowane, jeśli są dołączone do mikropasków (synchronicznie lub w postaci then, która jest wykonywana w tej samej turze).

+0

Dziękuję za przykład. Możesz przesłać go do dowolnego API opartego na wywołaniach zwrotnych, takiego jak w Node.js lub prostego żądania AJAX: jeśli otoczysz API opartym na oddzwonieniu obietnicą, a to wywołanie zwrotne zostanie nazwane w przyszłości i musisz odrzucić obietnicę, mają ten sam problem. W moim przypadku obleję Oboe obietnicą i zadzwonię do drugiego API opartego na oddzwonieniu i zawijam to również w obietnicy. Moja druga obietnica zostaje odrzucona, a następnie otrzymuję błąd powyżej.Więc jakie jest rozwiązanie tego? Niestety jestem bardzo ograniczony w systemie komentarzy StackOverflow, więc nie mogę tutaj napisać zbyt wiele. – LongFlick

+0

Lub: Nie mogę użyć obietnic, które zostaną odrzucone w przyszłości i są obsługiwane za pomocą Promise.all(). Głównym problemem jest to, że nie mam bezpośredniego catch() na obietnicy, ale w związku z tym w moim zaproszeniu Promise.all(). Jeśli zrobię catch() na mojej wewnętrznej obietnicy, to działa, ale nie chcę tego. Chcę go złapać w Promise.all(). Uważam, że tego nie rozumiem. Więc jakie jest rozwiązanie dla interfejsów API, w których mam obietnice, które wywołują interfejsy API, które są również obiecujące, ale nie zawierają obsługi catch(). Czy wszystkie mają obsługi catch()? Myślałem, że Promise.all(). Catch() był dokładnie tym, czego szukam – LongFlick

+0

@ LongFlick 'var p = Promise.reject(); p.catch (() => {});/* uwaga, że ​​nie zmieniliśmy p, jest to ta sama obietnica, właśnie dodaliśmy handler catch * /; p.then (...);/* nie więcej nieobsługiwanego zgłoszenia odrzucenia, wyraźnie zrezygnowaliśmy */' –