A estrela da vez é o Node.js! Este post parte do princípio de que o cangaceiro ou cangaceira já tenha um conhecimento, ainda que básico, do uso de Promises.

Error first-callback

Quem desenvolve há alguns anos na plataforma Node.js com certeza já aplicou diversas vezes o padrão error first-callback adotado pelas API’s assíncronas da plataforma. Vejamos um exemplo que cria um arquivo assíncronamente, inclusive você pode testá-lo em sua máquina:

// importa a função writeFile do módulo fs
const { writeFile } = require('fs');

// recebe o nome do arquivo, conteúdo e o callback 
// para lidar com a operação 
writeFile('arquivo.txt', 'conteúdo do arquivo', err => {
  
  // verifica se houve algum erro
  // Se houve, logada e aborta a função
  if(err) return console.log(err);

  // se não houve erro
  console.log('arquivo criado com sucesso!');
}); 

No entanto, não é raro o desenvolvedor querer lidar com operações assícronas como a que acabamos de ver utilizando Promises.

Promisificando a operação de escrita

Podemos promisificar o código anterior da seguinte maneira:

const { writeFile } = require('fs');

function criaArquivo(nome, conteudo) {

  return new Promise((resolve, reject) => {

    writeFile('arquivo.txt', 'conteúdo do arquivo', err => {
      if(err) return reject(err);
        resolve();
      }); 		
    });
}

criaArquivo()
  .then(() => console.log('arquivo criado com sucesso!'))
  .catch(err => console.log(err));

Excelente, mas termos que criar uma função auxiliar para converter uma API error first callback do Node.js para uma Promise pode ser um tanto tedioso. Uma solução seria alterar toda a API assíncrona do Node.js para trabalhar com Promises, mas isso quebraria toda a plataforma. Porém, a versão 8.0 do Node.js trouxe uma solução elegante para o problema.

A magia de util.promisify

Para melhorar a experiência do deenvolvedor foi introduzida na versão 8.0 do Node.js a função utilitária util.promisify. Vejamos o código anterior com este novo recurso:

// importou promisify do módulo util
const { promisify } = require('util');
const { writeFile } = require('fs');

// promisifica a função writeFile
const writeFilePromisificado = promisify(writeFile);

writeFilePromisificado('arquivo.txt', 'conteúdo arquivo')
  .then(() => console.log('arquivo criado com sucesso!'))
  .catch(err => console.log(err));

Ou, para tornar o código ainda mais enxuto:

const { promisify } = require('util');

// realizou o require já promisificando
const writeFile = promisify(require('fs').writeFile);

writeFile('arquivo.txt', 'conteúdo arquivo')
  .then(() => console.log('arquivo criado com sucesso!'))
  .catch(err => console.log(err));

Com util.promisify podemos transformar qualquer API error first callback em Promises sem termos a responsabilidade de criar um wrapper para isso. É win, win win!

Bônus

Com a transformação de API’s assíncronas do Node.js em Promises através de util.promisify podemos utilizar sem grande burocracia a sintaxe async/await:

const { promisify } = require('util');

// faz o require já promisificando
const writeFile = promisify(require('fs').writeFile);

(async function() {
  try {
    await writeFile('arquivo.txt', 'conteúdo arquivo');
    console.log('arquivo criado com sucesso!');
  } catch(err) {
    console.log(err);
  }
})();

Conclusão

O util.promisify é um recurso recente na plataforma Node.js que permite transformar suas API’s legadas em API’s que retornem Promises com um mínimo de impacto na plataforma.