2012-07-02 10 views
34

Używam the Q module dla Node.js w celu uniknięcia "piramidy losu" w scenariuszach, w których mam wiele kroków. Na przykład:Jak prawidłowo przerwać obietnicę łańcucha node.js za pomocą Q?

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(callback).end(); 
} 

Zasadniczo to wydaje się działać; jeśli błąd zostanie zgłoszony przez któryś z kroków zadania, jest on przekazywany do wywołania zwrotnego (chociaż byłbym mile widziany w ulepszeniach, ponieważ jestem nowy dla obietnicy node.js). Jednak mam problem, gdy trzeba wcześniej przerwać łańcuch zadań. Na przykład, jeśli wynik1 powodzeniem powrócił może chcę wywołać zwrotnego wcześnie i przerwać odpoczynek, ale moje próby, aby to zrobić nie udaje ...

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     if(result1) 
     {// the rest of the task chain is unnecessary 
      console.log('aborting!'); 
      callback(null, result1); 
      return null; 
     } 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     console.log('doing step 3...'); 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(callback).end(); 
} 

W tym przykładzie, widzę jak „przerwanie” i "wykonano krok 3 ..." wydrukowane.

Jestem pewna, że ​​po prostu nie rozumiem podstawowych zasad tutaj, więc doceniam każdą pomoc. Dzięki!

+0

Jednym rozwiązaniem znalazłem jest utworzenie osobnego łańcuch obietnica po pierwszym łańcuch może pęknąć. Niż w powyższym przykładzie instrukcja .ten z wynikiem 2 zostaje dołączona do Q.ncall dla kroku 2, zamiast dołączania do pierwotnej obietnicy. JEDNAKOLWIEK, główną wadą jest to, że pozbywa się jednej z głównych korzyści dla Q w mojej opinii: unikając piramidy zagłady! To wciąż lepsze niż żadne obietnice, ale nie podoba mi się rozwiązanie ... –

Odpowiedz

16

Wszelkie błędy, które są rzucane w łańcuchu obietnicy powoduje, że cały stos zostać przerwane wcześnie i kontroli jest podana na ścieżkę błędów powrotem. (w tym przypadku procedura obsługi fail()) Gdy wykryjesz pewien stan, który powoduje, że chcesz przerwać łańcuch obietnic, po prostu wyślij bardzo konkretny błąd, który przechwycisz w błąd i zignoruj ​​(jeśli tak, wyboru)

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     if(result1 == 'some failure state I want to cause abortion') 
     {// the rest of the task chain is unnecessary 
      console.log('aborting!'); 
      throw new Error('abort promise chain'); 
      return null; 
     } 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     console.log('doing step 3...'); 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(function(err) { 
     if (err.message === 'abort promise chain') { 
      // just swallow error because chain was intentionally aborted 
     } 
     else { 
      // else let the error bubble up because it's coming from somewhere else 
      throw err; 
     } 
    }) 
    .end(); 
} 
+17

Używasz wyjątków do sterowania przepływem, a zazwyczaj nie jest to zalecane. Rozwiązanie podane przez Krisa Kowala pozwala uniknąć tego problemu. –

+3

'return null' nie jest konieczne po' throw' – Pepijn

34

Jest to przypadek, w którym trzeba rozgałęzić, co oznacza zagnieżdżanie lub tworzenie podprogramu.

function doTask(task, callback) { 
    return Q.ncall(task.step1, task) 
    .then(function(result1) { 
     if (result1) return result1; 
     return Q.ncall(task.step2, task) 
     .then(function(result2) { 
      return Q.ncall(task.step3, task); 
     }) 
    }) 
    .nodeify(callback) 
} 

Albo

function doTask(task, callback) { 
    return Q.ncall(task.step1, task) 
    .then(function(result1) { 
     if (result1) { 
      return result1; 
     } else { 
      return continueTasks(task); 
     } 
    }) 
    .nodeify(callback) 
} 

function continueTasks(task) { 
    return Q.ncall(task.step2, task) 
    .then(function(result2) { 
     return Q.ncall(task.step3, task); 
    }) 
} 
+0

Czy to najlepsze podejście do rozgałęziania? Wydaje się, że to wprowadza wcięcie ponownie, gdy istnieje wiele gałęzi. Oto [przykład] (https://gist.github.com/svenjacobs/3f42bbaf4cbabe2b58b5), w którym wykonuję wiele operacji na plikach przy użyciu [q-io] (https://github.com/kriskowal/q-io). Najpierw sprawdzam, czy istnieje katalog, wymień pliki szukające określonego pliku i usuń go, jeśli znaleziono tylko jeden pasujący plik. Istnieje wiele warunków if, które powinny przerwać łańcuch. Używam specjalnej wartości zwracanej w celu sprawdzenia tego przypadku, ale muszę to sprawdzić w każdej funkcji. Czy to dobre podejście? –

+4

@SvenJacobs to, co opisujesz w tym przykładzie, jest dobrym przykładem wyjątków. Rozważmy https://gist.github.com/kriskowal/e98774443eb0f1653871 –

+2

Nadal mam problem z tym podejściem, ponieważ utrudnia obsługę błędów. Rzucanie łańcucha obietnic (odpowiedź Calvina Alvina) pozwala na posiadanie pojedynczego '.fail()', który dba o każdy błąd podczas przepływu. Pisanie obietnic w ten sposób (rozgałęzienie) zabiera mnie z powrotem do piekła oddzwaniania. – Pedro

2

wierzę, trzeba tylko odrzucić obietnicę wyrwania się z łańcucha obietnicy.

https://github.com/kriskowal/q/wiki/API-Reference#qrejectreason

również wydaje się .END() została zmieniona na .done()

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     if(result1) 
     {// the rest of the task chain is unnecessary 
      console.log('aborting!'); 
      // by calling Q.reject, your second .then is skipped, 
      // only the .fail is executed. 
      // result1 will be passed to your callback in the .fail call 
      return Q.reject(result1); 
     } 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     console.log('doing step 3...'); 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(callback).done(); 
}