Digamos que eu tenha um conjunto de Promise
s que esteja fazendo solicitações de rede, das quais uma falhará:
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
Digamos que eu queira esperar até que tudo isso termine, independentemente de uma ter falhado. Pode haver um erro de rede para um recurso sem o qual posso viver, mas que, se conseguir, desejo antes de prosseguir. Eu quero lidar com falhas de rede normalmente.
Como Promises.all
não deixa espaço para isso, qual é o padrão recomendado para lidar com isso, sem usar uma biblioteca de promessas?
javascript
promise
es6-promise
Nathan Hagen
fonte
fonte
allSettled
que satisfaz sua necessidade sem que você precise rolar a sua própria.Promise.all
rejeitará assim que qualquer promessa rejeitar, para que o idioma proposto não garanta que todas as promessas sejam cumpridas.Respostas:
Atualização, você provavelmente deseja usar o nativo interno
Promise.allSettled
:Como um fato interessante, esta resposta abaixo foi anterior ao adicionar esse método ao idioma:]
Claro, você só precisa de
reflect
:Ou com ES5:
Ou no seu exemplo:
fonte
reflect
é comum na ciência da computação? Você pode fazer um link para onde isso é explicado, como na wikipedia ou algo assim. Eu estava procurando muito,Promise.all not even first reject
mas não sabia pesquisar "Reflect". O ES6 deve ter umPromise.reflect
que é como "Promise.all, mas realmente todos"?Resposta semelhante, mas mais idiomática para o ES6, talvez:
Dependendo do (s) tipo (s) de valores retornados, os erros geralmente podem ser distinguidos com bastante facilidade (por exemplo, use
undefined
para "não me importo",typeof
para valores simples que não sejam objetosresult.message
,result.toString().startsWith("Error:")
etc.)fonte
.map(p => p.catch(e => e))
peça transforma todas as rejeições em valores resolvidos, portantoPromise.all
ainda espera que tudo termine, se as funções individuais resolverem ou rejeitarem, independentemente do tempo que levarem. Tente..catch(e => console.log(e));
nunca é chamado, porque isso nunca falhacatch
seja geralmente uma boa prática IMHO .e
e o retorna como um valor regular (sucesso). O mesmo quep.catch(function(e) { return e; })
apenas mais curto.return
está implícito.A resposta de Benjamin oferece uma grande abstração para resolver esse problema, mas eu esperava uma solução menos abstrata. A maneira explícita de resolver esse problema é simplesmente invocar
.catch
as promessas internas e retornar o erro do retorno de chamada.Dando um passo adiante, você pode escrever um manipulador de captura genérico parecido com este:
então você pode fazer
O problema é que os valores capturados terão uma interface diferente dos valores não capturados; portanto, para limpar isso, você pode fazer algo como:
Então agora você pode fazer isso:
Então, para mantê-lo seco, você obtém a resposta de Benjamin:
onde agora parece
Os benefícios da segunda solução são que ela é abstraída e SECA. A desvantagem é que você tem mais código e precisa se lembrar de refletir todas as suas promessas para tornar as coisas consistentes.
Eu caracterizaria minha solução como explícita e KISS, mas de fato menos robusta. A interface não garante que você saiba exatamente se a promessa foi bem-sucedida ou não.
Por exemplo, você pode ter isso:
Isso não será pego por
a.catch
, entãoNão há como saber qual foi fatal e qual não foi. Se isso é importante, convém aplicar e fazer uma interface que rastreie se foi bem-sucedida ou não (o que
reflect
faz).Se você deseja apenas manipular os erros normalmente, pode apenas tratar os erros como valores indefinidos:
No meu caso, não preciso saber o erro ou como ele falhou - apenas me importo se tenho o valor ou não. Vou deixar a função que gera a promessa se preocupar em registrar o erro específico.
Dessa forma, o restante do aplicativo pode ignorar seu erro, se desejar, e tratá-lo como um valor indefinido, se desejar.
Quero que minhas funções de alto nível falhem com segurança e não me preocupo com os detalhes de por que suas dependências falharam, e também prefiro o KISS ao DRY quando precisar fazer essa troca - e foi por isso que optei por não usar
reflect
.fonte
Promise
s. Enquanto vocêreflect
melhora a reutilização do código, ele também estabelece outro nível de abstração. Como a resposta de Nathan até agora recebeu apenas uma fração de votos positivos em relação à sua, pergunto-me se isso é uma indicação de um problema com a solução dele, que ainda não reconheci.new Promise((res, rej) => res(new Error('Legitimate error'))
não seria distinguívelnew Promise(((res, rej) => rej(new Error('Illegitimate error'))
? Ou ainda, você não seria capaz de filtrar porx.status
? Vou acrescentar este ponto à minha resposta para que a diferença seja mais claraPromise.all()
variante específica ; também fica a cargo do consumidor do Promise saber que uma promessa específica não será rejeitada, mas será engolir é erros. De fato, oreflect()
método poderia ser menos "abstrato" e mais explícito chamando-oPromiseEvery(promises).then(...)
. A complexidade da resposta acima, em comparação com a de Benjamin, deve dizer muito sobre essa solução.Existe uma proposta finalizada para uma função que pode realizar isso de forma nativa, em Javascript vanilla
Promise.allSettled
:, que chegou ao estágio 4, é oficializado no ES2020 e é implementado em todos os ambientes modernos . É muito semelhante àreflect
função nesta outra resposta . Aqui está um exemplo, na página da proposta. Antes, você teria que fazer:Usando em
Promise.allSettled
vez disso, o acima será equivalente a:Aqueles que usam ambientes modernos poderão usar esse método sem nenhuma biblioteca . Nesses, o seguinte snippet deve ser executado sem problemas:
Resultado:
Para navegadores mais antigos, há um polyfill compatível com especificações aqui .
fonte
Eu realmente gosto da resposta de Benjamin, e como ele basicamente transforma todas as promessas em sempre resolver, mas às vezes com erro como resultado. :)
Aqui está minha tentativa de sua solicitação, caso você esteja procurando alternativas. Esse método simplesmente trata os erros como resultados válidos e é codificado de maneira semelhante
Promise.all
:fonte
settle
. Temos isso também em bluebird, eu gosto de refletir melhor, mas esta é uma solução viável para quando você tiver isso em uma matriz.Promise
construtor corretamente (e evitar issovar resolve
)?Ele
Promise.all
engolirá qualquer promessa rejeitada e armazenará o erro em uma variável; portanto, ele retornará quando todas as promessas forem resolvidas. Em seguida, você pode refazer o erro ou fazer o que for. Dessa forma, acho que você obteria a última rejeição em vez da primeira.fonte
err.push(error)
erros, transformando-o em uma matriz e usando-o , para que todos os erros pudessem ocorrer.Eu tive o mesmo problema e resolvi-o da seguinte maneira:
Nesse caso
Promise.all
, aguardará que toda Promessa entreresolved
ourejected
declarem.E, com esta solução, estamos "interrompendo a
catch
execução" de maneira não-bloqueadora. Na verdade, não estamos parando nada, apenas retornamos oPromise
estado pendente que retorna outroPromise
quando é resolvido após o tempo limite.fonte
Promise.all
. Estou procurando uma maneira de ouvir quando todas as promessas foram invocadas, mas não as invoco eu mesmo. Obrigado.all()
faz isso, aguarda o cumprimento de todas as promessas ou a rejeição de pelo menos uma delas..all
aciona tudo.then
ou.all
chamada), mas são executadas quando criadas.Isso deve ser consistente com o modo como Q faz :
fonte
A resposta de Benjamin Gruenbaum é obviamente ótima. Mas também posso ver se o ponto de vista de Nathan Hagen com o nível de abstração parece vago. Ter propriedades curtas de objetos como
e & v
também não ajuda, mas é claro que isso pode ser alterado.Em Javascript, há um objeto Error padrão, chamado
Error
,. Idealmente, você sempre lança uma instância / descendente disso. A vantagem é que você pode fazerinstanceof Error
e sabe que algo é um erro.Então, usando essa idéia, aqui está minha opinião sobre o problema.
Capture o erro basicamente, se o erro não for do tipo Error, envolva o erro dentro de um objeto Error. A matriz resultante terá valores resolvidos ou objetos de erro que você pode verificar.
A instância dentro da captura, é para o caso de você usar alguma biblioteca externa que talvez tenha usado
reject("error")
, em vez dereject(new Error("error"))
.É claro que você poderia ter promessas se resolvesse um erro, mas nesse caso provavelmente faria sentido tratá-lo como um erro de qualquer maneira, como mostra o último exemplo.
Outra vantagem de fazer isso, a destruição da matriz é mantida simples.
Ao invés de
Você pode argumentar que a
!error1
verificação é mais simples que uma instância de, mas você também precisa destruir as duasv & e
.fonte
Em vez de rejeitar, resolva-o com um objeto. Você poderia fazer algo assim ao implementar a promessa
fonte
Eu acho que os seguintes oferece uma abordagem um pouco diferente ... comparar
fn_fast_fail()
comfn_slow_fail()
... embora este último não falha como tal ... você pode verificar se um ou ambosa
eb
é uma instância deError
ethrow
queError
se você quer que ele alcance ocatch
bloco (por exemploif (b instanceof Error) { throw b; }
). Veja o jsfiddle .fonte
Aqui está o meu costume
settledPromiseAll()
Comparado com
Promise.all
Se todas as promessas forem resolvidas, ele será executado exatamente como o padrão.
Se uma ou mais promessas forem rejeitadas, ela retornará a primeira rejeitada da mesma forma que a padrão, mas, ao contrário, aguarda que todas as promessas sejam resolvidas / rejeitadas.
Para os corajosos, poderíamos mudar
Promise.all()
:CUIDADO . Em geral, nunca alteramos os built-ins, pois isso pode quebrar outras bibliotecas JS não relacionadas ou entrar em conflito com alterações futuras nos padrões JS.
My
settledPromiseall
é compatível com versões anterioresPromise.all
e amplia sua funcionalidade.Pessoas que estão desenvolvendo padrões - por que não incluí-lo em um novo padrão Promise?
fonte
Promise.all
com o uso deasync/await
abordagem modernafonte
Eu faria:
fonte
Você pode executar sua lógica sequencialmente através do executor síncrono nsynjs . Ele será pausado em cada promessa, aguardará resolução / rejeição e atribuirá o resultado da resolução à
data
propriedade ou lançará uma exceção (para lidar com o que você precisará do bloco try / catch). Aqui está um exemplo:fonte
Eu tenho usado os seguintes códigos desde ES5.
A assinatura de uso é exatamente como
Promise.all
. A principal diferença é quePromise.wait
aguardará todas as promessas para terminar seus trabalhos.fonte
Sei que esta pergunta tem muitas respostas e tenho certeza (se não todas) que estão corretas. No entanto, foi muito difícil para mim entender a lógica / fluxo dessas respostas.
Então olhei para a Implementação Original
Promise.all()
e tentei imitar essa lógica - com exceção de não interromper a execução se uma Promessa falhar.Explicação:
- Faça um loop sobre a entrada
promisesList
e execute cada Promessa.- Não importa se a promessa foi resolvida ou rejeitada: salve o resultado da promessa em uma
result
matriz de acordo com oindex
. Salve também o status de resolução / rejeição (isSuccess
).- Depois que todas as promessas forem concluídas, retorne uma promessa com o resultado de todas as outras.
Exemplo de uso:
fonte
Promise.all
, há muitas coisas que darão errado. Sua versão não lida com entradas vazias, por exemplo.Não sei qual biblioteca de promessas você está usando, mas a maioria tem algo como all .
Edit: Ok, já que você deseja usar o ES6 simples sem bibliotecas externas, não existe esse método.
Em outras palavras: você precisa repetir suas promessas manualmente e resolver uma nova promessa combinada assim que todas as promessas forem cumpridas.
fonte