Todo programador, seja ele web ou não, nos últimos anos está se deparando com uma quantidade sem precedentes de mudanças nas linguagens de programação. Não é segredo que o JavaScript é, dentre estas linguagens, uma das que mais sofrem alterações drásticas em sua API constantemente.
Em 2015, tivemos a primeira grande alteração da API da linguagem com o ES6 e, desde então, a linguagem vem sofrendo cada vez mais melhorias para que suas funcionalidades sejam cada vez mais otimizadas e novos recursos sejam inseridos.
O processo de escolha, desenvolvimento e implementação destas funcionalidades é gerenciado pelo TC39, que é responsável por analisar, testar e verificar se todas as funcionalidades estão de acordo com o proposto. Todo o projeto é aberto e colaborativo! Então, isso significa que todos podem entrar lá e mandar uma Pull Request requisitando (ou até implementando) uma nova funcionalidade.
Desde o ES6 tivemos, mais ou menos, uma nova versão da especificação lançada por ano, e este ano não poderia ser diferente, o ES2018 está previsto para chegar em junho e já está oficializada pelo time.
Vamos dar uma olhada nas principais features desta nova versão.
Asynchronous Iteration
O ES6, dentre outras mudanças, nos trouxe os iterators. Estes objetos nada mais são do que estruturas que fornecem a funcionalidade de navegar entre itens em uma lista como, por exemplo, um array, e também seguem uma estrita convenção.
Todo iterator retorna o próximo item da lista da seguinte forma:
{ value: Any, done: Boolean }
E, além disso, cada iterator possui um método next() que nada mais faz do que retornar o objeto acima. Todos os arrays são – por definição – iterators, então podemos escrever algo desta forma:
let arr = [1, 2, 3, 4, 5] let iterator = arr[Symbol.iterator]() console.log(iterator.next().value) // 1 console.log(iterator.next().value) // 2 console.log(iterator.next().value) // 3 console.log(iterator.next().done) // false console.log(iterator.next().value) // 4 console.log(iterator.next().value) // 5 console.log(iterator.next().done) // true
O problema aqui é que todos os iterators são síncronos. A grande novidade do ES2018 é torná-los assíncronos. O que, basicamente, significa que cada chamada de next() irá retornar uma Promise que poderá ser resolvida, portanto o código anterior viraria algo assim:
const { value, done } = iterator.next() // Aqui temos um iterator síncrono normal asyncIterator.next() // isto nos retorna uma Promise<{ value, done }>
O que temos de fazer agora é utilizar a nova sintaxe for await of disponibilizada nessa mesma versão.
for await (const linha of readLines(arquivo)) { console.log(linha) }
Isto abre não só possibilidades maiores como iteração em arquivos linha a linha em tempo real ao invés de ter de esperar o arquivo todo ser carregado na memória antes de poder realizar qualquer ação sobre ele.
Mais detalhes podem ser vistos na documentação desta proposta.
Rest/Spread para objetos
Desde o ES6 já temos as chamadas rest/spread properties, que são basicamente os destructuring assignments utilizadas para decompor arrays em variáveis individuais.
let a, b, rest [a, b] = [10, 20] // a = 10 e b = 20
Agora um exemplo usando rest:
let a, b, rest [a, b, ...rest] = [1, 2, 3, 4, 5, 6] console.log(a) // 1 console.log(b) // 2 console.log(rest) // [3, 4, 5, 6]
Para ser curto e grosso, o ES2018 está introduzindo a mesma possibilidade para objetos. Portanto, poderemos fazer isso se utilizarmos rest:
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 } x // 1 y // 2 z // { a: 3, b: 4 }
E spread, igualmente:
let n = { x, y, ...z } console.log(n) // { x: 1, y: 2, a: 3, b: 4 }
Esta é uma proposta poderosa, porque nos abre várias portas, por exemplo, para filtrar um objeto, removendo suas propriedades desnecessárias de uma forma imutável. Imagine que temos um objeto de usuário, e queremos remover o e-mail deste objeto. Hoje fazemos assim:
const user = { firstName: 'Lucas', lastName: 'Santos', twitter: '_staticvoid', city: 'São Paulo', email: 'lucas.santos@santos.me', } const usuarioSemEmail = Object.assign({}, user) delete usuarioSemEmail.email
O que não é necessariamente errado, mas temos problemas quando temos de executar esta tarefa para vários campos. Com a nova proposta poderemos fazer algo assim:
const {email, ...usuarioSemEmail} = user
Assim como toda a proposta, esta também tem uma documentação oficial que você pode conferir.
Promise.finally()
Esta foi uma feature muito requisitada desde o início das promises no JavaScript. Uma curiosidade sobre elas é que, apesar de serem oficialmente suportadas no ES6, as promises são um padrão de projeto muito utilizado pelos desenvolvedores JavaScript muito antes disso através de bibliotecas como o Bluebird e o Q.
Como todo desenvolvedor JavaScript, provavelmente já deve estar careca de saber, uma promise é definida por ser um constructo que executa uma ação assíncrona e encadeia um callback utilizando o método then() o que, por sua vez, leva uma função com os parâmetros retornados da execução. Caso tenhamos um problema com a tarefa assíncrona, utilizamos o método catch() para recuperar o erro e tratá-lo.
A grande novidade, que já era implementada nessas bibliotecas anteriores, é o uso do finally() da mesma forma como fazemos nos blocos try/catch atuais.
Hoje temos isto:
promise .then(result => {···}) .catch(error => {···})
Com o ES2018 teremos um novo método finally() que será responsável por executar qualquer função ao final da execução da tarefa assíncrona, falhando ou não.
promise .then(result => {···}) .catch(error => {···}) .finally(() => {...})
Desta forma vamos continuar tendo a implementação padrão, com o acréscimo de um método que será sempre executado no final. Veja a documentação oficial para maiores detalhes!
RegExp: grupos nomeados
O ES2018 vai trazer não só esta, mas também outras mudanças no interpretador de Regex do JavaScript. Dentre elas, teremos:
- Flag s (dotAll) suportada;
- Suporte a lookbehind assertions, o que é algo bastante útil quando estamos buscando palavras dentro de strings ou entre delimitadores;
- Escapes unicode (propriedade \p{…} e \P{…}) suportada
Mas, na minha opinião, a mais importante de todas é o suporte a grupos nomeados. Quem já utilizou Regex deve saber que podemos separar nossas asserções em grupos, desta forma:
let regex = /(?\d{4})(?\d{2})(?\d{2})/u let resultado = regex.exec('2018-04-10') // resultado[0] => '2018-04-10' // resultado[1] => '2018' // resultado[2] => '04' // resultado[3] => '10'
Mas veja que é um pouco complicado termos que trabalhar somente com os números dos índices. Para isso, uma nova sintaxe foi criada que nos permite dar nomes a cada um destes grupos:
let regex = /(?<ano>\d{4})(?<mes>\d{2})(?<dia>\d{2})/u let resultado = regex.exec('2018-04-10') // resultado.groups.ano => '2018' // resultado.groups.mes => '04' // resultado.groups.dia => '10'
A busca por índices continuará funcionando normalmente também.
Além destas features, outras propostas estão sendo discutidas no TC39 para melhorar ainda mais a interação com o interpretador RegExp dentro do JavaScript.
Com isso, fechamos grande parte do que está por vir neste ano no ES2018 e ficamos aguardando ansiosamente a chegada da nova especificação para que possamos, cada vez mais, realizar nossas tarefas de forma mais eficiente utilizando uma das linguagens que mais cresce no planeta!