"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Atribuição Segura

Atribuição Segura

Publicado em 2024-11-05
Navegar:171

Safe Assignment

Houve um grande burburinho hoje sobre a nova proposta para um operador de atribuição seguro (?=) em JavaScript. Adoro como o JavaScript melhorou ao longo do tempo, mas esse também é um problema que encontrei em alguns casos recentemente. Eu deveria preparar um exemplo rápido de implementação como uma função, certo?

Caso você não tenha lido a proposta, aqui está o que ela propõe:

const [error, value] ?= maybeThrows();

O novo operador ?= seria equivalente a chamar o lado direito da atribuição em um bloco try/catch, retornando um array. O primeiro valor da matriz retornada seria um erro se algo fosse lançado dentro da atribuição, e o segundo valor seria o resultado da atribuição se nada fosse lançado.

Um aborrecimento comum de tentativa/captura

Frequentemente encontro códigos que parecem muito feios em relação a blocos de atribuição e try/catch. Coisas assim:

let errorMsg;

try {
  maybeThrow();
} catch (e) {
  errorMsg = "An error message";
}

Para acessar errorMsg fora do bloco try/catch usando const ou let você deve defini-lo fora do bloco.

Uma implementação não assíncrona

O caso mais fácil aqui é lidar com funções não assíncronas. Eu consegui preparar
alguns casos de teste e uma função chamada tryCatch em pouco tempo:

function tryCatch(fn, ...args) {
  try {
    return [undefined, fn.apply(null, args)]
  } catch (e) {
    return [e, undefined];
  }
}

function throws() {
  throw new Error("It threw");
}

// returns a sum
// prints [ undefined, 2 ]
console.log(tryCatch(Math.sqrt, 4));

// returns an error
// prints [ Error: 'It threw', undefined ]
console.log(tryCatch(throws));

tryCatch chama a função com os argumentos fornecidos agrupados em um bloco try/catch. Ele retorna apropriadamente [indefinido, resultado] se nada for lançado dentro da função e [erro, indefinido] se algo for lançado.

Observe que você também pode usar uma função anônima com tryCatch se ainda não tiver uma função pronta para chamar.

console.log(tryCatch(() => {
  throw new Error("It threw");
}));

Lidando com funções assíncronas

As funções assíncronas ficam um pouco mais complicadas. Uma ideia que tive inicialmente foi escrever
uma versão completamente assíncrona, talvez chamada de asyncTryCatch, mas onde está o desafio nisso. Esta é uma exploração completamente inútil! Aqui está uma implementação de tryCatch que funciona com funções assíncronas e não assíncronas:

function tryCatch(fn, ...args) {
  try {
    const result = fn.apply(null, args);

    if (result.then) {
      return new Promise(resolve => {
          result
            .then(v => resolve([undefined, v]))
            .catch(e => resolve([e, undefined]))  
      }); 
    }

    return [undefined, result];
  } catch (e) {
    return [e, undefined];
  }
}

function throws() {
  throw new Error("It threw");
}

async function asyncSum(first, second) {
  return first   second;
}

async function asyncThrows() {
  throw new Error("It throws async");
}

// returns a sum
// prints [ undefined, 2 ]
console.log(tryCatch(Math.sqrt, 4));

// returns an error
// prints [ Error: 'It threw', undefined ]
console.log(tryCatch(throws));

// returns a promise resolving to value
// prints [ undefined, 3 ]
console.log(await tryCatch(asyncSum, 1, 2));

// returns a promise resolving to error
// prints [ Error: 'It throws async', undefined ]
console.log(await tryCatch(asyncThrows));

Parece muito com a versão original, mas com algum código baseado em Promise
jogado em boa medida. Com esta implementação você pode chamar tryCatch ao chamar uma função não assíncrona e, em seguida, chamar await tryCatch ao chamar uma função assíncrona.

Vejamos a parte da promessa:

if (result.then) {
  return new Promise(resolve => {
      result
        .then(v => resolve([undefined, v]))
        .catch(e => resolve([e, undefined]))    
  }); 
}

if (result.then) verifica se a função fornecida (chamada com apply) retornou uma promessa. Se isso acontecer, precisaremos retornar uma promessa.

Chamar result.then(v => resolve([undefined, v])) faz com que a promessa seja resolvida para o valor retornado pela função fornecida, se nada for gerado.

.catch(e => resolve([e, undefined])) é um pouco mais complicado. Eu escrevi originalmente
é como .catch(e => rejeitar([e, indefinido])), mas isso causa um erro não detectado
cair fora do tryCatch. Precisamos resolver aqui porque estamos retornando um
array, não gerando um erro.

E finalmente

Eu regularmente tenho casos em que preciso tentar/pegar, mas me sinto como
O bloco try/catch explícito ocupa muito espaço e é irritante para atribuições de escopo. Não tenho certeza se vou usá-lo ou não, mas esta foi uma pequena exploração divertida.

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/nalanj/safe-assignment-44g7?1 Se houver alguma violação, entre em contato com [email protected] para excluí-lo
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3