Closed pgstudies22 closed 2 years ago
Oi @pgstudies22! O ideal é que sim, sejam funções puras. Isso vai evitar bugs difíceis de achar e dores de cabeça de maneira geral xD Claro que você pode fazer mutação local ali, se achar mais fácil, desde que não modifique os objetos originais: crie sempre novos e modifique eles, se for o caso :)
Um outro caso para o map
principalmente é também quando você precisa fazer uma chamada async com cada item do array: a função pode retornar uma Promise (pode usar async/await
, inclusive), mas tem sempre que lembrar que o map
vai sempre retornar antes das Promises serem resolvidas.
Então se vc precisa fazer uma ação async
com cada item de um array, pode usar o map
, mas você vai precisar usar um Promise.all
ou Promise.allSettled
, passando o resultado do map
, para aguardar todas as chamadas async
terminarem corretamente antes de fazer alguma coisa com o resultado, já que o map
é síncrono :)
Um outro caso para o map principalmente é também quando você precisa fazer uma chamada async com cada item do array...
Seria mais ou menos isso?
const pokemonPromises = Array
.from({ length: 3 })
.map(async (_, index) => {
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${index + 1}`)
return res.json()
})
Promise
.allSettled(pokemonPromises)
.then(console.log) // [{status: 'fulfilled', value: {…}}, ...]
.catch(error => console.log('error:', error))
Mas aí fiquei na dúvida...
Eu preciso checar res.ok
antes de retornar a promise do res.json()
.
Nesse caso, colocaria um try catch
dentro do map
?
<button>Click-me</button>
const button = document.querySelector('button')
button.addEventListener('click', () => {
const pokemonPromises = Array
.from({ length: 3 })
.map(async (_, index) => {
try {
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${index + 1}`)
if (!res.ok) {
throw new Error('Falha na conexão. Não foi possível buscar os dados.')
}
return res.json()
} catch (error) {
console.log('error no catch do try/catch:', error)
return error
}
})
Promise
.allSettled(pokemonPromises)
.then(console.log)
.catch(error => console.log('error:', error))
})
No exemplo acima, inseri um <button>
na marcação HTML e, antes de clicar no botão, simulei offline network
no devtools.
Ao clicar no botão, o callback do método catch
foi executado 3x.
Pelo que entendi, como houve falha na conexão, o fetch lançou um erro, que foi pego no catch (do try catch), e o return inseriu o erro no array que o map estava gerando.
Por último, assim que o array de promises foi resolvido, ele foi exibido no console.
Minhas dúvidas são as seguintes...
Pq o if (!res.ok)
não foi executado?
O método catch
encadeado no then
não foi executado pq não houve lançamento de erro por parte do then
?
@fdaciuk
Oi @pgstudies22 ! Vamos lá: seu exemplo está correto: ao clicar no botão, as 3 Promises serão criadas e, ao serem resolvidas, o resultado será exibido.
O if (!res.ok)
não foi executado pq o fetch
deu erro, já que você não tinha rede. O fetch só cai no catch se acontecer erro de rede, que foi o seu caso. Se tivesse rede, mas o request respondesse com erro, aí sim ele passaria naquele if :)
E o .catch nunca será executado no allSettled, pq esse método não quebra! Todas as Promises resolvidas com sucesso ou com falha só caem no then, e cada resultado recebe um status de fulfilled (se finalizou com sucesso) ou rejected (se finalizou com erro).
Ficou claro?
Tem ainda um último detalhe: como você tratou o erro na primeira promise (com try/catch), todas as Promises irão estar com status fulfilled, já que o erro que estourar nunca vai chegar no allSettled, a menos que você remova o try/catch, ou dê throw em um erro customizado dentro do catch :)
Ahhh, acho que estou começando a entender professor =D
E o .catch nunca será executado no allSettled, pq esse método não quebra!
Que massa! Estou conhecendo esse método agora =)
E realmente, eu testei remover try/catch e na falha da rede, as promises contém status rejected e reason com a mensagem do erro.
button.addEventListener('click', () => {
const pokemonPromises = Array
.from({ length: 3 })
.map(async (_, index) => {
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${index + 1}`)
return res.json()
})
Promise
.allSettled(pokemonPromises)
.then(p => console.log('p:', p)) // [{ status: 'rejected', reason: '...' }, {…}, {…}]
})
Seria ótimo assim, um if a menos no código.
Mas, é meio que mandatório testar o !res.ok
pra fazer algo caso haja rede mas o status HTTP não tenha vindo no range 200-299, né?
Será que há alguma forma mais interessante do que if para checar .ok
?
Ao fazer o request com um endpoint que não existe, como é um caso em que baseado em uma condição um valor precisa ser retornado, usei ternário.
try {
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${index + 1}/sadasdas`)
return res.ok ? res.json() : new Error('Não foi possível obter os dados.')
} catch (error) {
console.log('error no catch do try/catch:', error)
return error
}
Alguns possíveis problemas que vejo nessa abordagem (posso estar equivocado)...
throw
;res.ok
, o código "quebra" (entre aspas pq eu refatoraria para if ao invés de aninhar ternários).😬
@fdaciuk
Mas, é meio que mandatório testar o !res.ok pra fazer algo caso haja rede mas o status HTTP não tenha vindo no range 200-299, né?
Depende de como você vai tratar os erros. Você pode tratar e dar throw em um erro diferente, customizado, pra ficar mais fácil de tratar na hora de usar o allSettled
:)
Será que há alguma forma mais interessante do que if para checar .ok?
Não, esse tem que ser assim mesmo :)
Alguns possíveis problemas que vejo nessa abordagem (posso estar equivocado)... 1) Não usei o throw; 2) Se mais checagens tiverem que ser feitas no res.ok, o código "quebra" (entre aspas pq eu refatoraria para if ao invés de aninhar ternários).
Exatamente! Eu faria assim:
if (!res.ok) {
throw new Error('Não foi possível obter os dados.')
}
return res.json()
Show professor. Muito obrigado pelos esclarecimentos.
O suporte que vc dá aos alunos é fora da curva. Praticamente uma mentoria, hahah.
Se quiser fechar a issue, por mim tudo bem. Minhas dúvidas foram sanadas =)
@fdaciuk
Massa @pgstudies22, que bom que tudo ficou claro! :D Qualquer dúvida, fique à vontade para perguntar :)
Olá professor, tudo bem?
Callbacks dos métodos
map
,filter
ereduce
devem ser funções puras?Pergunto pq sempre que vejo alguém executando efeito colateral em algum callback desses métodos, parece que tem algo estranho. Que o método não deveria ser usado para isso.
Mas ao mesmo tempo, não encontrei na doc desses métodos no MDN algo que sustente a tese de que deveriam ser funções puras.
Na doc do map, por exemplo, é falado que ele não deve ser usado quando o array que ele retorna não é aproveitado. Mas já vi por aí callback em que há mutação em variável/propriedade externa antes do
return
do item que será adicionado no array que o map gera...@fdaciuk