O que há de novo no Node.js 15?

Estamos compartilhando a tradução do artigo, que contém detalhes sobre os novos recursos da 15ª versão do Node.js.


A versão 15 do Node.js foi lançada em 20 de outubro de 2020. As principais mudanças incluem:



  • modo de lançamento em desvios não tratados
  • recursos de linguagem V8 8.6
  • NPM 7
  • suporte QUIC experimental
  • N-API Versão 7
  • finalização da API Async Local Storage


Vamos examinar mais de perto o que são essas inovações e como podem ser usadas.



Usando NVM para Visão Geral do Nó



No artigo anterior, examinamos as instruções para usar NVM (Node Version Manager) para gerenciar versões de Node.js e NPM. Temos o Node.js 12.16.0 e o NPM 6.14.8 instalados em nosso ambiente. Ao executar o nvm install node , instalamos o Node.js 15.4.0 e o NPM7.0.15.



Temos duas janelas abertas, uma com Node.js 12 e outra com Node.js 15.



Na janela do node12 :



$ nvm use 12
Now using node v12.16.0 (npm v6.14.8)
      
      





Na janela node15 :



$ nvm use 15
Now using node v15.4.0 (npm v7.0.15)
      
      





Agora podemos investigar esta versão.



Modo de lançamento na rejeição de promessa não tratada



O evento unhandledRejection é gerado sempre que uma promessa é rejeitada e um manipulador de erro não é anexado à promessa durante o loop de eventos. A partir do Node.js 15, o modo padrão para unhandledRejection foi alterado de warn para throw . No modo de lançamento , se o gancho unhandledRejection não estiver definido, o unhandledRejection será lançado como uma exceção não detectada pelo método catch .



Crie um programa para rejeitar a promessa com uma mensagem de erro:



function myPromise() {
  new Promise((_, reject) =>
    setTimeout(
      () =>
        reject({
          error: 'The call is rejected with an error',
        }),
      1000
    )
  ).then((data) => console.log(data.data));
}

myPromise();
      
      





Quando você executa este código em uma janela node12 , uma longa mensagem de aviso é exibida:



$ node myPromise.js
(node:79104) UnhandledPromiseRejectionWarning: #<Object>
(node:79104) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:79104) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.Users that have an unhandledRejection hook should see no change in behavior, and it’s still possible to switch modes using the --unhandled-rejections=mode process flag.
      
      





A execução deste código na janela node15 gera um erro UnhandledPromiseRejection :



$ node myPromise.js
node:internal/process/promises:227
          triggerUncaughtException(err, true /* fromPromise */);
          ^[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "#<Object>".] {
  code: 'ERR_UNHANDLED_REJECTION'
}
      
      





Adicione um manipulador de erros ao branch then no código abaixo ( .catch (( erro ) => console.log ( erro .error)) também funciona).



function myPromise() {
  new Promise((_, reject) =>
    setTimeout(
      () =>
        reject({
          error: 'The call is rejected with an error',
        }),
      1000
    )
  ).then(
    (data) => console.log(data.data),
    (error) => console.log(error.error)
  );
}

myPromise();
      
      





Agora o código é executado corretamente em ambas as janelas ( node12 e node15 ):



$ node myPromise.js
The call is rejected with an error
      
      





É recomendado escrever um manipulador de erros para promessas. No entanto, pode haver casos em que os erros não são detectados pelo método catch. É recomendável configurar o gancho unhandledRejection para detectar erros em potencial.



function myPromise() {
  new Promise((_, reject) =>
    setTimeout(
      () =>
        reject({
          error: 'The call is rejected with an error',
        }),
      1000
    )
  ).then((data) => console.log(data.data));
}

myPromise();

process.on('unhandledRejection', (reason, promise) => {
  console.log('reason is', reason);
  console.log('promise is', promise);
  // Application specific logging, throwing an error, or other logic here
});
      
      





O gancho unhandledRejection funciona tanto no Node.js 12 quanto no Node.js 15. Depois de instalado, o unhandledRejection é tratado conforme necessário.



$ node myPromise.js
reason is { error: 'The call is rejected with an error' }
promise is Promise { <rejected> { error: 'The call is rejected with an error' } }
      
      





V8 8.6 Novos recursos de linguagem



V8, mecanismo JavaScript, atualizado de 8.4 para 8.6. versão. Além de vários ajustes para melhorar o desempenho, o novo V8 tem os seguintes recursos:



  • Promise.any () e AggregateError (de V8 8.5)
  • aguarde setTimeout e AbortController (experimental)
  • String.prototype.replaceAll () (a partir de V8 8.5)
  • Operadores de atribuição lógica && = , || = e ?? = (a partir de V8 8.5)


Promise.any () e AggregateError



Primeiro, vamos dar uma olhada no método Promise.all () existente .



Promise.all () pega um iterável de promessas como entrada e retorna uma única promessa, que é executada como uma matriz dos resultados das promessas de entrada.



O programa a seguir chama Promise.all () em duas promessas resolvidas:



function myPromise(delay) {
  return new Promise((resolve) =>
    setTimeout(
      () =>
        resolve({
          data: The data from ${delay} ms delay,
        }),
      delay
    )
  );
}

async function getData() {
  try {
    const data = await Promise.all([myPromise(5000), myPromise(100)]);
    console.log(data);
  } catch (error) {
    console.log(error);
  }
}

getData();

      
      





Promise.all () retorna uma promessa que será cumprida quando todas as promessas de entrada forem resolvidas ou se o iterável não contiver promessas:



$ node myPromise.js
[
  { data: 'The data from 5000 ms delay' },
  { data: 'The data from 100 ms delay' }
]
      
      





O programa a seguir chama Promise.all () em duas promessas rejeitadas.



function myPromise(delay) {
  return new Promise((_, reject) =>
    setTimeout(
      () =>
        reject({
          error: The error from ${delay} ms delay,
        }),
      delay
    )
  );
}

async function getData() {
  try {
    const data = await Promise.all([myPromise(5000), myPromise(100)]);
    console.log(data);
  } catch (error) {
    console.log(error);
  }
}

getData();
      
      





Promise.all () rejeita imediatamente qualquer rejeição da promessa de entrada ou qualquer erro no momento da execução, retornando uma mensagem sobre este erro:



$ node myPromise.js
{ error: 'The error from 100 ms delay' }
      
      





Promise.any () é um novo método em Node.js 15. É o oposto de Promise.all () . Promise.any () aceita um iterável contendo objetos Promise. E, assim que uma das Promessas no iterável for bem-sucedida, o método retornará uma única promessa com o valor da promessa cumprida.



O programa a seguir chama Promise.any () em duas promessas resolvidas:



function myPromise(delay) {
  return new Promise((resolve) =>
    setTimeout(
      () =>
        resolve({
          data: The error from ${delay} ms delay,
        }),
      delay
    )
  );
}

async function getData() {
  try {
    const data = await Promise.any([myPromise(5000), myPromise(100)]);
    console.log(data);
  } catch (error) {
    console.log(error);
    console.log(error.errors);
  }
}

getData();
      
      





Promise.any () retorna a primeira promessa resolvida:



$ node myPromise.js
{ data: 'The error from 100 ms delay' }
      
      





O programa a seguir chama Promise.any () em duas promessas rejeitadas:



function myPromise(delay) {
  return new Promise((_, reject) =>
    setTimeout(
      () =>
        reject({
          error: The error from ${delay} ms delay,
        }),
      delay
    )
  );
}

async function getData() {
  try {
    const data = await Promise.any([myPromise(5000), myPromise(100)]);
    console.log(data);
  } catch (error) {
    console.log(error);
    console.log(error.errors);
  }
}

getData();
      
      





Se as promessas no iterável falharem, ou seja, todas as promessas feitas são rejeitadas, a promessa retornada é rejeitada com AggregateError , uma nova subclasse de Error que agrupa erros individuais.



$ node myPromise.js
[AggregateError: All promises were rejected]
[
  { error: 'The error from 5000 ms delay' },
  { error: 'The error from 100 ms delay' }
]
      
      





Aguarde setTimeout e AbortController



Nos exemplos anteriores, usamos setTimeout dentro de uma chamada de promessa.



SetTimeout em WindowOrWorkerGlobalScope usa um retorno de chamada. No entanto, timers / promises fornecem uma versão prometida de setTimeout que pode ser usada com async / await.



const { setTimeout } = require('timers/promises');

async function myPromise(delay) {
  await setTimeout(delay);
  return new Promise((resolve) => {
    resolve({
      data: The data from ${delay} ms delay,
    });
  });
}

async function getData() {
  try {
    const data = await Promise.any([myPromise(5000), myPromise(100)]);
    console.log(data);
  } catch (error) {
    console.log(error);
    console.log(error.errors);
  }
}

getData();
      
      





AbortController é um objeto JavaScript que permite abortar uma ou mais solicitações da web à vontade. Fornecemos exemplos de uso do AbortController em outro artigo sobre useAsync .



Ambos aguardam setTimeout e AbortController são recursos experimentais.



String.prototype.replaceAll ()



Vamos dar uma olhada no método String.prototype.replace () existente .



replace () retorna uma nova string com algumas ou todas as correspondências de padrão substituídas com o substituto. O padrão pode ser uma string ou uma expressão regular. O espaço reservado pode ser uma string ou uma função chamada para cada correspondência.



Se o padrão for uma string, apenas a primeira ocorrência será substituída.



'20+1+2+3'.replace('+', '-');
      
      





Usar este operador resultará em “20–1 + 2 + 3” .



Para substituir todos os "+" por "-", você precisa usar uma expressão regular.



'20+1+2+3'.replace(/\+/g, '-');
      
      





Usar o operador acima resultará em “20-1-2-3” .



O método replaceAll () é novo no Node.js 15. Ao usá-lo, não precisamos usar uma expressão regular. Este método retorna uma nova string com todas as correspondências de padrão substituídas pelo espaço reservado. O padrão pode ser uma string ou uma expressão regular, e o marcador de posição pode ser uma string ou uma função chamada para cada correspondência.



Graças ao método replaceAll () , não precisamos usar uma expressão regular para substituir todos os "+" por "-".



'20+1+2+3'.replaceAll('+', '-');
      
      





A execução deste operador dá “20-1-2-3” .



Operadores de atribuição lógica && =, || = e ?? =



O operador de atribuição lógica AND ( x && = y ) realiza uma operação de atribuição apenas se x for verdadeiro.



x && = y é equivalente a x && (x = y) , mas não equivalente a x = x && y .



let x = 0;
let y = 1;

x &&= 0; // 0
x &&= 1; // 0
y &&= 1; // 1
y &&= 0; // 0
      
      





O operador de atribuição lógica OR (x || = y) realiza uma operação de atribuição apenas se x for falso.



x || = y é equivalente ax || (x = y) mas não equivalente a x = x || a .



let x = 0;
let y = 1;

x ||= 0; // 0
x ||= 1; // 1
y ||= 1; // 1
y ||= 0; // 1
      
      





O operador de atribuição lógica nullish (x ?? = y) realiza uma operação de atribuição apenas se x for NULL ( nulo ou indefinido ).



x ?? = y é equivalente ax ?? (x = y) , e não equivalente ax = x ?? a .



let x = undefined;
let y = '';

x ??= null; // null
x ??= 'a value'; // "a value"
y ??= undefined; // ""
y ??= null; // ""
      
      





Outras mudanças



Além do modo de lançamento em rejeição de promessa não tratada e novos recursos de linguagem V8 8.6, Node.js 15 tem as seguintes alterações:



NPM 7 : muitas alterações, incluindo instalação automática de dependências de pares, melhorias em pacotes e arquivos yarn.lock, suporte a espaço de trabalho , etc. Tudo isso é descrito neste artigo por referência .



QUIC : suporte experimental para a camada de transporte UDP, que é o protocolo principal para HTTP / 3. O QUIC inclui segurança integrada com TLS 1.3, controle de fluxo, correção de erros, migração de conexão e multiplexação.



N-API Versão 7: API para criar addons personalizados. É independente do tempo de execução JavaScript subjacente e é compatível com Node.js.



Aprimoramento da API Async Local Storage : Fornece a capacidade de registro mais moderno e análise de recursos para aplicativos de grande escala.



Conclusão



A nova versão do Node.js 15 possui um grande número de novos recursos e melhorias, incluindo algumas bastante significativas.



Experimente a nova versão e prepare-se para atualizar projetos.



Obrigado pela atenção! Espero que o artigo tenha sido útil para você.



All Articles