Atualizar:
Para ajudar os futuros telespectadores deste post, criei esta demonstração da resposta da pluma .
Questão:
Meu objetivo parece bastante direto.
step(1)
.then(function() {
return step(2);
}, function() {
stepError(1);
return $q.reject();
})
.then(function() {
}, function() {
stepError(2);
});
function step(n) {
var deferred = $q.defer();
//fail on step 1
(n === 1) ? deferred.reject() : deferred.resolve();
return deferred.promise;
}
function stepError(n) {
console.log(n);
}
O problema aqui é que, se eu falhar na etapa 1, ambos stepError(1)
AND stepError(2)
serão acionados. Se não o fizer return $q.reject
, stepError(2)
não serei demitido, mas step(2)
sim, o que eu entendo. Eu realizei tudo, exceto o que estou tentando fazer.
Como escrevo promessas para poder chamar uma função de rejeição, sem chamar todas as funções na cadeia de erros? Ou existe outra maneira de conseguir isso?
Aqui está uma demonstração ao vivo para que você possa trabalhar com algo.
Atualizar:
Eu meio que resolvi isso. Aqui, estou capturando o erro no final da cadeia e passando os dados para, reject(data)
para saber o problema a ser tratado na função de erro. Na verdade, isso não atende aos meus requisitos, porque não quero depender dos dados. Seria ruim, mas no meu caso, seria mais limpo passar um retorno de chamada de erro para a função, em vez de depender dos dados retornados para determinar o que fazer.
Demonstração ao vivo aqui (clique).
step(1)
.then(function() {
return step(2);
})
.then(function() {
return step(3);
})
.then(false,
function(x) {
stepError(x);
}
);
function step(n) {
console.log('Step '+n);
var deferred = $q.defer();
(n === 1) ? deferred.reject(n) : deferred.resolve(n);
return deferred.promise;
}
function stepError(n) {
console.log('Error '+n);
}
Promise.prototype.catch()
exemplos no MDN mostram a solução para exatamente os mesmos problemas.Respostas:
O motivo pelo qual seu código não funciona conforme o esperado é que ele está realmente fazendo algo diferente do que você pensa que faz.
Digamos que você tenha algo como o seguinte:
Para entender melhor o que está acontecendo, vamos fingir que este é um código síncrono com
try
/catch
blocks:O
onRejected
manipulador (o segundo argumento dethen
) é essencialmente um mecanismo de correção de erros (como umcatch
bloco). Se um erro for lançadohandleErrorOne
, ele será capturado pelo próximo bloco de captura (catch(e2)
) e assim por diante.Obviamente, isso não é o que você pretendia.
Digamos que queremos que toda a cadeia de resolução falhe, não importa o que dê errado:
Nota: Podemos deixar o local
handleErrorOne
onde está, porque ele será invocado apenas se forstepOne
rejeitado (é a primeira função na cadeia, portanto, sabemos que se a cadeia for rejeitada neste momento, ela poderá ser apenas por causa da promessa dessa função) .A mudança importante é que os manipuladores de erro para as outras funções não fazem parte da principal cadeia de promessas. Em vez disso, cada etapa tem sua própria "sub-cadeia" com uma
onRejected
que é chamada apenas se a etapa foi rejeitada (mas não pode ser alcançada diretamente pela cadeia principal).A razão pela qual isso funciona é que ambos
onFulfilled
eonRejected
são argumentos opcionais para othen
método. Se uma promessa for cumprida (ou seja, resolvida) e a próximathen
da cadeia não tiver umonFulfilled
manipulador, a cadeia continuará até que exista uma com esse manipulador.Isso significa que as duas linhas a seguir são equivalentes:
Mas a seguinte linha não é equivalente às duas acima:
A biblioteca de promessas do Angular
$q
é baseada naQ
biblioteca do kriskowal (que possui uma API mais rica, mas contém tudo o que você pode encontrar$q
). Os documentos da API da Q no GitHub podem ser úteis. Q implementa a especificação Promises / A + , que detalha comothen
e o comportamento da resolução da promessa funciona exatamente.EDITAR:
Lembre-se também de que, se você quiser sair da cadeia em seu manipulador de erros, ele precisará retornar uma promessa rejeitada ou lançar um erro (que será capturado e envolvido automaticamente em uma promessa rejeitada). Se você não retornar uma promessa,
then
agrupe o valor de retorno em uma promessa de resolução para você.Isso significa que, se você não devolver nada, estará efetivamente retornando uma promessa resolvida para o valor
undefined
.fonte
if you don't return anything, you are effectively returning a resolved promise for the value undefined.
Thanks @plumastepOne().then(stepTwo, handleErrorOne)
`stepOne (). then (null, handleErrorOne) .then (stepTwo)` Estes são realmente equivalentes? Eu acho que em caso de rejeição nastepOne
segunda linha de código será executado,stepTwo
mas o primeiro só será executadohandleErrorOne
e interrompido. Ou eu estou esquecendo de alguma coisa?Um pouco atrasado para a festa, mas esta solução simples funcionou para mim:
Isso permite que você quebrar fora da cadeia.
fonte
.then(user => { if (user) return Promise.reject('The email address already exists.') })
.then(user => { if (user) throw 'The email address already exists.' })
O que você precisa é de uma
.then()
cadeia de repetição com um caso especial para começar e um caso especial para terminar.A habilidade é fazer com que o número da etapa do caso de falha passe para um manipulador de erros final.
step(1)
incondicionalmente..then()
com os seguintes retornos de chamada:.then()
sem manipulador de sucesso e um manipulador de erro final.Você pode escrever a coisa toda à mão, mas é mais fácil demonstrar o padrão com funções generalizadas nomeadas:
Vejo demonstração
Observe como
step()
, o adiado é rejeitado ou resolvidon
, tornando esse valor disponível para os retornos de chamada no próximo.then()
na cadeia. Uma vezstepError
chamado, o erro é repetidamente repetido até ser tratado porfinalError
.fonte
Ao rejeitar, você deve passar um erro de rejeição e agrupar manipuladores de erro de etapa em uma função que verifica se a rejeição deve ser processada ou "retrocedida" até o final da cadeia:
O que você veria no console:
Aqui está um código de trabalho https://jsfiddle.net/8hzg5s7m/3/
Se você tiver um tratamento específico para cada etapa, seu wrapper pode ser algo como:
então sua corrente
fonte
Se bem entendi, você deseja que apenas seja exibido o erro da etapa que falhou, certo?
Isso deve ser tão simples quanto mudar o caso de falha da primeira promessa para isso:
Ao retornar
$q.reject()
no caso de falha da primeira etapa, você está rejeitando essa promessa, que faz com que o errorCallback seja chamado na 2ªthen(...)
.fonte
step(2)
. Agora eu tentei novamente, isso não está acontecendo. Estou tão confuso.return step(2);
apenas deve ser chamada sempre que forstep(1)
resolvida com êxito.return $q.reject()
, a corrente continuará. Neste caso,return response
estraguei tudo. Veja isto: jsbin.com/EpaZIsIp/6/edithttp://jsbin.com/EpaZIsIp/20/edit
Ou automatizado para qualquer número de etapas:
http://jsbin.com/EpaZIsIp/21/edit
fonte
deferred.reject(n)
então eu estou recebendo aviso de que promessa rejeitado com um objeto nonErrorTente ro usar isso como libs:
https://www.npmjs.com/package/promise-chain-break
fonte
Se você deseja resolver esse problema usando async / waitit:
fonte
Anexe manipuladores de erro como elementos de cadeia separados diretamente à execução das etapas:
ou usando
catch()
:Nota: Esse é basicamente o mesmo padrão sugerido pela pluma em sua resposta, mas usando o nome do OP.
fonte
Promise.prototype.catch()
Exemplos encontrados no MDN abaixo são muito úteis.(A resposta aceita menciona
then(null, onErrorHandler)
que é basicamente o mesmo quecatch(onErrorHandler)
.)fonte
A melhor solução é refatorar sua cadeia de promessas para usar os ES6 aguardados. Em seguida, você pode simplesmente retornar da função para pular o restante do comportamento.
Eu tenho batido minha cabeça contra esse padrão por mais de um ano e usar a espera é o paraíso.
fonte
Use um módulo SequentialPromise
Intenção
Forneça um módulo cuja responsabilidade seja executar solicitações sequencialmente, enquanto rastreia o índice atual de cada operação de maneira ordinal. Defina a operação em um Padrão de Comando para flexibilidade.
Participantes
execute
método para encadear e rastrear cada operação. SequentialPromise retorna uma cadeia de promessas de todas as operações executadas.execute
método enquanto passa uma lista ordinal de opções para cada operação.Consequências
Use SequentialPromise quando o comportamento ordinal da resolução Promise for necessário. SequentialPromise rastreará o índice para o qual uma promessa foi rejeitada.
Implementação
Essência
SequentialPromise
fonte
Se em algum momento você retornar,
Promise.reject('something')
você será jogado no bloco de captura da promessa.Se a primeira promessa não retornar nenhum resultado, você obterá apenas 'Sem resultado' no console.
fonte