Fluxo Assíncrono
O JavaScript é uma linguagem
single-threade
Consequências
- Deixa o usuário sem ação durante todo o tempo para a realização de uma operação;
- Não permite que o servidor seja capaz de realizar algum outra requisição até que a atual termine,,,,,,.
Solução
- Buscar em tornar operações muito extensas em operações assíncronas, dessa forma tornando o código mais performático e bem escrito.
Callbacks
Assim que uma operação for concluída a callback será executada
Exemplo:
const fs = require('fs');
fs.readFile('./arquivo.txt', (err, content) => {
if (err) {
console.error(`Erro ao ler o arquivo: ${err.message}`);
return;
}
console.log(`Arquivo lido. Conteúdo: ${content.toString('utf8')}`);});
No código acima é utilizado uma node-style callback
(formato de callback nativa do Node.js)
O que está acontecendo:
- No primeiro parâmetro é passado o arquivo que sera lido;
- No segundo parâmetro é passado uma callback para tratar a resposta da função
- Assim é possível tratar a resposta de forma diferente, dependendo de se ocorreu algum erro em meio ao processo ou se tudo ocorreu como deveria
O lado ruim das callback:
- Nem tudo são flores, o principal problema em se usar esse método é que seu resultado estará unicamente naquela callback, o que gera
a necessidade de executar uma coisa dentro da outra
Exemplo:
Callback hell
const fs = require('fs');
fs.readFile('file1.txt', (err, file1Content) => {
if (err) return console.log(Erro ao ler arquivo 1: ${err.message});
console.log(Lido file1.txt com ${file1Content.byteLength} bytes);
fs.readFile('file2.txt', (err, file2Content) => {
if (err) return console.log(Erro ao ler o arquivo 2: ${err.message});
console.log(Lido file2.txt com ${file2Content.byteLength} bytes);
fs.readFile('file3.txt', (err, file3Content) => {
if (err) return console.log(Erro ao ler o arquivo 3: ${err.message});
console.log(Lido file3.txt com ${file3Content.byteLength} bytes);
});
});
});
Como mostrado no código acima, a legibilidade se torna muito mais complexa devido a necessidade de criar uma callback dentro de outra callback até concluir sua tarefa.
Possível solução:
const fs = require('fs');
const file3Callback = (err, file3Content) => {
if (err) return console.log(Erro ao ler o arquivo 3: ${err.message});
console.log(Lido file3.txt com ${file3Content.byteLength} bytes);
};
const file2Callback = (err, file2Content) => {
if (err) return console.log(Erro ao ler o arquivo 2: ${err.message});
console.log(Lido file2.txt com ${file2Content.byteLength} bytes);
fs.readFile('file3.txt', file3Callback);
};
const file1Callback = (err, file1Content) => {
if (err) return console.log(Erro ao ler arquivo 1: ${err.message});
console.log(Lido file1.txt com ${file1Content.byteLength} bytes);
fs.readFile('file2.txt', file2Callback);
};
fs.readFile('file1.txt', file1Callback);
Como tentativa de tornar o código mais legível, são criadas varias funções com o único intuito de chamar a próxima callback, apesar de torná-lo um pouco mais legível, ainda não é performático.
Promises
A cereja do bolo para as callbacks, melhorando sua legibilidade e deixando seu código muito mais intuitivo.
- Sua principal diferença é que ao invés de uma única callback receber tanto o sucesso quanto o erro, ela tera duas callback com funções únicas, uma lidará com o erro e a outra com o sucesso.
Ok, mas como essa mágica acontece?
Observe os dois casos a seguir:
function dividirNumerosSemPromises(num1, num2) {
if (num2 == 0) throw new Error("Não pode ser feito uma divisão por zero");
return num1 / num2;
}
try {
const resultado = dividirNumeros(2, 1);
console.log(`resultado: ${resultado}`);
} catch (e) {
console.log(e.message);
}
Na função síncrona dividirNumerosSemPromises
o resultado da callback já é tratado dentro da função.
function dividirNumerosComPromises(num1, num2) {
const promise = new Promise((resolve, reject) => {
if (num2 == 0) reject(new Error("Não pode ser feito uma divisão por zero"));
const resultado = num1 / num2;
resolve(resultado)
});
return promise;
}
dividirNumeros(2, 1)
.then(result => console.log(sucesso: ${result}))
.catch(err => console.log(erro: ${err.message}));
Agora na função assíncrona dividirNumerosComPromises
o resultado não é tratado dentro da função, mas sim onde ela está sendo chamada. Assim, com uma mesma função é possível tratar a resposta em inúmeras formas diferentes
Then e resolve x Catch e reject
Caso não tenha percebido, no segundo caso essas duas palavrinhas são utilizadas, mas o que elas significam?
- Then: forma de tratar o sucesso de uma callback, pode ser utilizado varias vezes no mesmo contexto;
- Catch: igualmente ao then, porém sua tarefa é tratar o erro.
Estrutura da Promese:
const p = new Promise((resolve, reject) => {
// Aqui é onde vamos realizar a lógica que precisamos
// para "tentar cumprir" a promessa
});
Ao escrevê-la, não se esqueça de utilizar a palavra-chave new
e da arrow function como parâmetro da Promese
.
const fs = require('fs');
function readFilePromise (fileName) {
return new Promise((resolve, reject) => {
fs.readFile(fileName, (err, content) => {
if (err) return reject(err);
resolve(content);
});
});
}
Perceba, no exemplo acima estamos utilizando do modulo interno fs
apenas com o intuito de ilustrar, o importante aqui é perceber como o resolve
e o reject
são utilizados.
readFilePromise('./file.txt') // A função me promete que vai ler o arquivo
.then((content) => { // Caso ela cumpra o que prometeu
console.log(Lido arquivo com ${content.byteLength} bytes); // Escrevo o resultado no console
})
.catch((err) => { // Caso ela não cumpra o que prometeu
console.error(Erro ao ler arquivo: ${err.message}); // Escrevo o erro no console
});
Sei que é muita coisa, mas aos pouquinhos você vai pegar o jeito :)
Top comments (0)