Tarefas de IO em NodeJs são por padrão assíncronas, ou seja: o fluxo principal não fica em espera do retorno destas tarefas.
const rows
fs.readFile('data.csv', (err, data) => {
rows = data
})
Como a rotina principal não espera o retorno para atribuir a uma variável algum valor,
é necessário o uso de um callback
ao final da operação, que se encarregara de efetuar
as ações necessárias, quando o recurso estiver disponível.
function readJSON(filename, callback){
fs.readFile(filename, 'utf8', function (err, res){
if (err) return callback(err)
try {
callback(null, JSON.parse(res))
} catch (ex) {
callback(ex)
}
})
}
- legibilidade muito ruim
- várias checagens de erros no código
- fora dos controles de fluxo convencionais
- não retorna valores, as variáveis necessitam ser referenciadas dentro do callback para fazer "assign"
Promise é um objeto usado para processamento assíncrono. Um Promise (de "promessa") representa um valor que pode estar disponível agora, no futuro ou nunca...
Um Promise está um destes estados:
- pending (pendente): Estado inicial, que não foi realizada nem rejeitada.
- fulfilled (realizada): sucesso na operação.
- rejected (rejeitado): falha na operação.
- settled (estabelecida): Que foi realizada ou rejeitada.
function readJSON(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, 'utf8', (err, res) => {
if (err) {
reject(err)
return
}
try {
resolve( JSON.parse(res) )
} catch (ex) {
reject(ex)
}
})
})
}
// calbacks here
let products
readJSON("producs.json")
.then( json => { // if end with success
products = json
})
.catch(err => { // if end with fail
console.error(err)
})
Promisses podem ser encadeadas de maneira mais legível do que callbacks e o tratamento de errors pode ser feito em um único ponto, para uma cadeia de promisses.
readJSON("producs.json")
.then(json => { // if end with success
return otherPromisseFunction(json)
})
.then(json => { // if 2nd end with success
return andAnotherPromisseFunction(json)
})
.catch(err => { // if any promisse in chain fail
console.error(err)
})
Houve uma melhora na legibilidade e no tratamento de erros, e Promises também retornam valores, que podem ser associados a variáveis fora do escopo sem que haja necessidade da promise instaciá-la ou referenciá-la.
- fora dos controles de fluxo convencionais
Yield
é uma palavra reservada que define pontos de retorno em uma function generator
.
A tradução de yield seria entrega
, fornece
, provê
, isso ajuda a entender um pouco mais
o seu propósito.
function* foo() {
const index = 0;
while (index <= 2)
yield index++;
}
const iterator = foo();
console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
A cada chamada de next()
o próximo yield
é retornado
São funções que podem ser pausadas e retornadas de onde foram pausadas.
function* foo() {
let index = 0;
while (index <= 3){
yield index++;
if(index === 2)
return "valor"
}
}
const iterator = foo();
console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: "valor", done: true }
console.log(iterator.next()); // { value: undefined, done: true }
A palavra chave yield
indica pontos de pausa e retorno da função,
mas se houver um return
a função terminará quando o alcançar.
É possivel usar o yield
para passagem de parâmetros
function* logGenerator() {
console.log(yield);
console.log(yield);
console.log(yield);
}
const gen = logGenerator();
// the first call of next executes from the start of the function
// until the first yield statement
gen.next();
gen.next('pretzel'); // pretzel
gen.next('california'); // california
gen.next('mayonnaise'); // mayonnaise
Com libs como co
é possivel controlar promisses e generators através da palavra-chave `yield```
co(function*() {
const json = yield readJSON("producs.json")
})
muito mais legível, e também pode ser usado com coleções
Como array
const [ products, users ] = yield [
readJSON("producs.json"),
readJSON("users.json"),
]
Ou como object
const { products, users } = yield {
products: readJSON("producs.json"),
users: readJSON("users.json"),
}