2015-06-08 10 views
6

jest to wspólny wzór, że kaskada całej listy źródeł danych z pierwszego sukcesu zerwania łańcucha tak:Wdrożenie fallback za pomocą obietnic

var data = getData1(); 
if (!data) data = getData2(); 
if (!data) data = getData3(); 

et cetera. jeśli getDataN() funkcje są asynchroniczne, jednak prowadzi nas do 'piekła zwrotnej':

var data; 
getData1(function() { 
    getData2(function() { 
     getData3(function() { alert('not found'); }) 
    }) 
}); 

gdzie implementacje może wyglądać tak:

function getData1(callback) { 
    $.ajax({ 
     url: '/my/url/1/', 
     success: function(ret) { data = ret }, 
     error: callback 
    }); 
} 

... obietnicami I spodziewałby się napisać coś takiego:

$.when(getData1()) 
    .then(function (x) { data = x; }) 
    .fail(function() { return getData2(); }) 
    .then(function (x) { data = x; }) 
    .fail(function() { return getData3(); }) 
    .then(function (x) { data = x; }); 

gdzie drugi .then rzeczywiście odnosi się do wartości zwracanej pierwszy .fail, która sama w sobie jest obietnicą, a którą zrozumiałem, była przykuta jako dane wejściowe do kolejnego kroku łańcucha.

Wyraźnie się mylę, ale jaki jest właściwy sposób, aby to napisać?

+0

'getData1' powinien powrócić' $ .ajax' obietnicy. – hsz

+0

Pierwsza myśl: możesz dalej przekazywać obietnicę (jako drugi parametr), po sukcesie wypełniasz obietnicę, po niepowodzeniu uruchamiasz wywołanie zwrotne. –

+0

Myślę, że nie można tego zrobić na funkcji asynchronicznej. Dlaczego nie korzystać z rekurencyjnego wywołania zwrotnego i prostego jeśli-else? – dann

Odpowiedz

11

W większości obiecują bibliotekami, można łańcuch .fail() lub .catch() jak w użytkownika @ mido22 odpowiedź, ale jQuery .fail() nie „obsługiwać” błąd jako takie. Gwarantuje to zawsze przekazanie obietnicy wejściowej (z niezmienionym stanem), która nie pozwoliłaby na wymagane "zerwanie" kaskady, jeśli/kiedy dojdzie do sukcesu.

Jedyną metodą obietnicy jQuery, która może zwrócić obietnicę z innym stanem (lub inną wartością/powodem), jest .then().

W związku z tym można napisać łańcuch, który kontynuuje błąd, określając kolejny etap jako program obsługi błędów na każdym etapie.

function getDataUntilAsyncSuccess() { 
    return $.Deferred().reject() 
     .then(null, getData1) 
     .then(null, getData2) 
     .then(null, getData3); 
} 
//The nulls ensure that success at any stage will pass straight through to the first non-null success handler. 

getDataUntilAsyncSuccess().then(function (x) { 
    //"success" data is available here as `x` 
}, function (err) { 
    console.log('not found'); 
}); 

Ale w praktyce, może bardziej typowo utworzyć tablicę funkcji lub obiektów danych, które są wywoływane z kolei za pomocą metody Array .reduce().

Na przykład:

var fns = [ 
    getData1, 
    getData2, 
    getData3, 
    getData4, 
    getData5 
];  

function getDataUntilAsyncSuccess(data) { 
    return data.reduce(function(promise, fn) { 
     return promise.then(null, fn); 
    }, $.Deferred().reject());// a rejected promise to get the chain started 
} 

getDataUntilAsyncSuccess(fns).then(function (x) { 
    //"success" data is available here as `x` 
}, function (err) { 
    console.log('not found'); 
}); 

Albo, jak to chyba lepsze rozwiązanie tutaj:

var urls = [ 
    '/path/1/', 
    '/path/2/', 
    '/path/3/', 
    '/path/4/', 
    '/path/5/' 
];  

function getDataUntilAsyncSuccess(data) { 
    return data.reduce(function(promise, url) { 
     return promise.then(null, function() { 
      return getData(url);// call a generalised `getData()` function that accepts a URL. 
     }); 
    }, $.Deferred().reject());// a rejected promise to get the chain started 
} 

getDataUntilAsyncSuccess(urls).then(function (x) { 
    //"success" data is available here as `x` 
}, function (err) { 
    console.log('not found'); 
}); 
+0

to jest niesamowite @ Roamer-1888. To fascynujące, że nie widziałem żadnych przykładów tego rodzaju rozwiązania, ale jest to bardzo czyste. Przyjmę to jako rozwiązanie, gdy sprawdziłem, że działa.+1 – ekkis

+0

Kilka razy odpowiedziałem na podobne pytanie, ale wszystkie pytania były bardzo niepodobnie sformułowane, dlatego prawdopodobnie są trudne do znalezienia. Prawie musisz znać odpowiedź, aby wiedzieć, czego szukać, dlatego wciąż dostarczam nowych odpowiedzi, a nie linków do poprzednich. –

+1

BTW, kredyt trafia do @Bergi za postawienie mnie na pierwszym miejscu. Niestety, nie ma czasu na szukanie linku. –

Powiązane problemy