Existe alguma diferença entre:
const [result1, result2] = await Promise.all([task1(), task2()]);
e
const t1 = task1();
const t2 = task2();
const result1 = await t1;
const result2 = await t2;
e
const [t1, t2] = [task1(), task2()];
const [result1, result2] = [await t1, await t2];
javascript
async-await
Escondido
fonte
fonte
Respostas:
Para os fins desta resposta, usarei alguns métodos de exemplo:
res(ms)
é uma função que leva um número inteiro de milissegundos e retorna uma promessa que é resolvida após muitos milissegundos.rej(ms)
é uma função que leva um número inteiro de milissegundos e retorna uma promessa que rejeita depois de muitos milissegundos.A chamada
Exemplo 1res
inicia o temporizador. UsandoPromise.all
para aguardar alguns atrasos será resolvido após o término de todos os atrasos, mas lembre-se de que eles são executados ao mesmo tempo:Mostrar snippet de código
Isso significa que
Promise.all
será resolvido com os dados das promessas internas após 3 segundos.Mas,
Exemplo 2Promise.all
tem um comportamento "falhar rápido" :Mostrar snippet de código
Se você usar
Exemplo 3async-await
, terá que aguardar que cada promessa seja resolvida sequencialmente, o que pode não ser tão eficiente:Mostrar snippet de código
fonte
unhandledrejection
erros. Você nunca vai querer usar isso. Adicione isso à sua resposta.Primeira diferença - falha rapidamente
Concordo com a resposta de @ zzzzBov, mas a vantagem "falhar rápido" do Promise.all não é apenas a única diferença. Alguns usuários nos comentários perguntam por que usar Promise.all quando é apenas mais rápido no cenário negativo (quando alguma tarefa falha). E eu pergunto por que não? Se eu tenho duas tarefas paralelas assíncronas independentes e a primeira é resolvida em muito tempo, mas a segunda é rejeitada em muito pouco tempo, por que deixar o usuário esperar pela mensagem de erro "tempo muito longo" em vez de "tempo muito curto"? Em aplicações da vida real, devemos considerar um cenário negativo. Mas tudo bem - nesta primeira diferença, você pode decidir qual alternativa usar Promise.all vs. multiple aguardam.
Segunda diferença - tratamento de erros
Mas, ao considerar o tratamento de erros, DEVE usar o Promise.all. Não é possível manipular corretamente erros de tarefas paralelas assíncronas acionadas com espera múltipla. No cenário negativo, você sempre terminará com
UnhandledPromiseRejectionWarning
e,PromiseRejectionHandledWarning
embora use o try / catch em qualquer lugar. É por isso que Promise.all foi projetado. É claro que alguém poderia dizer que podemos suprimir que os erros usandoprocess.on('unhandledRejection', err => {})
eprocess.on('rejectionHandled', err => {})
, mas não é uma boa prática. Encontrei muitos exemplos na internet que não consideram o tratamento de erros para duas ou mais tarefas paralelas assíncronas independentes ou o consideram de maneira errada - basta usar try / catch e esperar que ele consiga detectar erros. É quase impossível encontrar boas práticas. É por isso que estou escrevendo esta resposta.Resumo
Nunca use múltiplo aguarde por duas ou mais tarefas paralelas assíncronas independentes, pois você não poderá lidar com erros seriamente. Sempre use Promise.all () para este caso de uso. Async / waitit não substitui Promessas. É muito bonito como usar promessas ... código assíncrono é escrito em estilo de sincronização e podemos evitar vários
then
promessas.Algumas pessoas dizem que usando Promise.all () não podemos lidar com erros de tarefas separadamente, mas apenas com erros da primeira promessa rejeitada (sim, alguns casos de uso podem exigir tratamento separado, por exemplo, para registro). Não é problema - consulte o título "Adição" abaixo.
Exemplos
Considere esta tarefa assíncrona ...
Quando você executa tarefas em um cenário positivo, não há diferença entre Promise.all e vários aguardam. Ambos os exemplos terminam
Task 1 succeed! Task 2 succeed!
após 5 segundos.Quando a primeira tarefa leva 10 segundos no cenário positivo e a tarefa segundos leva 5 segundos no cenário negativo, existem diferenças nos erros emitidos.
Já devemos notar aqui que estamos fazendo algo errado ao usar vários esperam em paralelo. Obviamente, para evitar erros, devemos lidar com isso! Vamos tentar...
Como você pode ver para tratar com êxito do erro, precisamos adicionar apenas uma captura à
run
função e o código com lógica de captura está no retorno de chamada ( estilo assíncrono ). Não precisamos manipular erros dentro darun
função porque a função assíncrona faz automaticamente - prometer rejeição datask
função causa rejeição darun
função. Para evitar retorno de chamada, podemos usar o estilo de sincronização (assíncrono / aguardar + tentar / capturar),try { await run(); } catch(err) { }
mas neste exemplo não é possível porque não podemos usarawait
no encadeamento principal - ele pode ser usado apenas na função assíncrona (é lógico, porque ninguém quer bloquear a linha principal). Para testar se o tratamento funciona no estilo de sincronização , podemos chamarrun
função de uma outra função assíncrono ou uso IIFE (Imediatamente Invoked Função Expressão):(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
.Essa é apenas uma maneira correta de executar duas ou mais tarefas paralelas assíncronas e manipular erros. Você deve evitar exemplos abaixo.
Podemos tentar manipular o código acima de várias maneiras ...
... nada foi pego porque ele lida com código de sincronização, mas
run
é assíncrono... Wtf? Vimos, em primeiro lugar, que o erro da tarefa 2 não foi tratado e, posteriormente, foi capturado. Enganador e ainda cheio de erros no console. Inutilizável dessa maneira.
... O mesmo que acima. O usuário @Qwerty, em sua resposta excluída, perguntou sobre esse comportamento estranho que parece ter sido detectado, mas também há erros não tratados. Nós capturamos o erro porque run () é rejeitado on-line com a palavra-chave wait e pode ser capturado usando try / catch ao chamar run (). Também recebemos erro não tratado porque estamos chamando a função de tarefa assíncrona de forma síncrona (sem palavra-chave aguardada) e essa tarefa é executada fora da função run () e também falha fora. É semelhante quando não somos capazes de lidar com erro try / catch ao chamar alguma função sync qual parte do código é executado em setTimeout ...
function test() { setTimeout(function() { console.log(causesError); }, 0); }; try { test(); } catch(e) { /* this will never catch error */ }
.... "apenas" dois erros (o terceiro está faltando), mas nada foi detectado.
Adição (manipule os erros da tarefa separadamente e também o erro de primeira falha)
... observe que neste exemplo eu usei negativeScenario = true para ambas as tarefas para melhor demonstração do que acontece (
throw err
é usado para disparar o erro final)fonte
Geralmente, o uso de
Promise.all()
solicitações de execução "assíncronas" em paralelo. O usoawait
pode ser executado em paralelo OU ser "sincronizado".As funções test1 e test2 abaixo mostram como
await
executar async ou sync.test3 mostra
Promise.all()
que é assíncrono.jsfiddle com resultados programados - abra o console do navegador para ver os resultados dos testes
Comportamento de sincronização . NÃO roda em paralelo, leva ~ 1800ms :
Comportamento assíncrono . É executado em paralelo, leva ~ 600ms :
Comportamento assíncrono . Funciona em paralelo, leva ~ 600ms :
TLDR; Se você estiver usando,
Promise.all()
ele também "falhará rapidamente" - pare de executar no momento da primeira falha de qualquer uma das funções incluídas.fonte
Você pode verificar por si mesmo.
Neste violino , realizei um teste para demonstrar a natureza do bloqueio
await
, ao contrário doPromise.all
que iniciará todas as promessas e enquanto um aguarda, ele continuará com os outros.fonte
t1 = task1(); t2 = task2()
e depois usarawait
para os dois,result1 = await t1; result2 = await t2;
como na pergunta dele, em oposição ao que você está testando e que está usandoawait
na chamada originalresult1 = await task1(); result2 = await task2();
. O código em sua pergunta inicia todas as promessas de uma só vez. A diferença, como mostra a resposta, é que as falhas serão relatadas mais rapidamente com oPromise.all
caminho.No caso de aguardar Promise.all ([task1 (), task2 ()]); "task1 ()" e "task2 ()" serão executados em paralelo e aguardarão até que ambas as promessas sejam concluídas (resolvidas ou rejeitadas). Considerando que, no caso de
T2 só será executado depois que T1 terminar a execução (tiver sido resolvido ou rejeitado). T1 e t2 não serão executados em paralelo.
fonte