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'); // mayonnaiseCom 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"),
  }