Как правильно вырваться из цепочки обещаний?

Исходя из вопроса здесь: jQuery цепочка и каскадирование тогда и тогда, и когда и принятый ответ.

Я хочу сломать цепочку обещаний в определенный момент, но пока не нашел правильный путь. Об этом несколько сообщений , но я все еще потерян.

Взяв пример кода из исходного вопроса:

Menus.getCantinas().then(function(cantinas){ // `then` is how we chain promises Menus.cantinas = cantinas; // if we need to aggregate more than one promise, we `$.when` return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas)); }).then(function(meals, sides){ // in jQuery `then` can take multiple arguments Menus.sides = sides; // we can fill closure arguments here Menus.meals = meals; return Menus.getAdditives(meals, sides); // again we chain }).then(function(additives){ Menus.additives = additives; return Menus; // we can also return non promises and chain on them if we want }).done(function(){ // done terminates a chain generally. // edit HTML here }); 

Как бы я сломал цепочку, если cantinas.length == 0 ? Я бы не хотел получать питание, ни добавки, честно говоря, я бы хотел назвать какой-то «пустой результат» обратного вызова. Я пробовал следующее, что очень уродливо (но работает …). Научите меня правильному пути. Это по-прежнему действительный результат, поэтому не «провал» как таковой, просто пустой результат, который я бы сказал.

 var emptyResult = false; Menus.getCantinas().then(function(cantinas){ Menus.cantinas = cantinas; if (cantinas.length == 0) { emptyResult = true; return "emptyResult"; //unuglify me } return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas)); }).then(function(meals, sides){ if (meals == "emptyResult") return meals; //look at my ugliness... Menus.sides = sides; Menus.meals = meals; return Menus.getAdditives(meals, sides); }).then(function(additives){ if (additives == "emptyResult") return additives; Menus.additives = additives; return Menus; }).done(function(){ if (emptyResult) //do empty result stuff else // normal stuff }); 

    Во-первых, я думаю, что лучше сказать, что вы пытаетесь «обходить» (часть) цепочки обещаний, а не «ломать» ее.

    Как вы говорите, тестирование на «emptyResult» в нескольких местах довольно уродливо. К счастью, более элегантный механизм доступен, придерживаясь того же общего принципа, что не выполняет какую-либо цепочку обещаний.

    Альтернативный механизм заключается в использовании отказа от обещаний для управления streamом, а затем для повторного определения конкретных условий ошибки (-ов) позже в цепочке и возврата его на путь успеха.

     Menus.getCantinas().then(function(cantinas) { Menus.cantinas = cantinas; if(cantinas.length == 0) { return $.Deferred().reject(errMessages.noCantinas); } else { return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas)); } }).then(function(meals, sides) { Menus.sides = sides; Menus.meals = meals; return Menus.getAdditives(meals, sides); }).then(function(additives) { Menus.additives = additives; return Menus; }).then(null, function(err) { //This "catch" exists solely to detect the noCantinas condition //and put the chain back on the success path. //Any genuine error will be propagated as such. //Note: you will probably want a bit of safety here as err may not be passed and may not be a string. return (err == errMessages.noCantinas) ? $.when(Menus) : err; }).done(function(Menus) { // with no cantinas, or with everything }); var errMessages = { 'noCantinas': 'no cantinas' }; 

    С положительной стороны, я считаю, что отсутствие гнездования позволяет лучше понять естественный путь успеха. Кроме того, для меня, по крайней мере, этот шаблон потребует минимального умственного манипулирования, чтобы приспособиться к дальнейшим обходам, если это необходимо.

    С нижней стороны этот рисунок несколько менее эффективен, чем у Берги. В то время как основной путь имеет такое же количество обещаний, как и у cantinas.length == 0 ‘s, путь cantinas.length == 0 требует еще один (или один на обход, если были закодированы несколько байпасов). Кроме того, этот шаблон требует надежного повторного обнаружения определенных условий (состояний) ошибки – следовательно, объект errMessages который некоторые могут найти, умаляет.

    Похоже, что вы хотите разветвиться , а не ломаться – вы хотите продолжить, как обычно, до done . Хорошим свойством обещаний является то, что они не только цепочки, но также могут быть вложенными и невзирая без ограничений. В вашем случае вы можете просто поместить часть цепочки, которую вы хотите «сломать» в своем if состоянии:

     Menus.getCantinas().then(function(cantinas) { Menus.cantinas = cantinas; if (cantinas.length == 0) return Menus; // break! // else return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas)) .then(function(meals, sides) { Menus.sides = sides; Menus.meals = meals; return Menus.getAdditives(meals, sides); }).then(function(additives) { Menus.additives = additives; return Menus; }); }).done(function(Menus) { // with no cantinas, or with everything }); 

    Для людей, использующих встроенные обещания в браузере, и ищет способ остановить цепочку обещаний, не информируя всех потребителей о случае отклонения, запуская любые прикованные или catch или бросая любые ошибки Uncaught (in promise) , вы можете использовать следующие:

     var noopPromise = { then: () => noopPromise, catch: () => noopPromise } function haltPromiseChain(promise) { promise.catch(noop) return noopPromise } // Use it thus: var p = Promise.reject("some error") p = haltPromiseChain(p) p.catch(e => console.log(e)) // this never happens 

    В принципе, noopPromise – это базовый интерфейс с пропущенным promiseм, который выполняет функции цепочки, но никогда не выполняет никаких функций. Это зависит от того, что, по-видимому, браузер использует утиную типизацию, чтобы определить, является ли что-то promise, поэтому YMMV (я тестировал это в Chrome 57.0.2987.98), но если это станет проблемой, возможно, вы можете создать реальный экземпляр обещания и средний его методы и методы улова.

    Давайте будем гением компьютера.