paint-brush
Tudo o que você precisa saber sobre promessas, possibilidades e avaliação preguiçosapor@austingil
1,653 leituras
1,653 leituras

Tudo o que você precisa saber sobre promessas, possibilidades e avaliação preguiçosa

por Austin Gil5m2023/01/20
Read on Terminal Reader

Muito longo; Para ler

No ano novo, vou mostrar como fazer 'Promessas' para ser mais preguiçoso. Vejamos um exemplo básico de `Promessa' . Aqui, tenho uma função chamada sleep que leva um tempo em milissegundos e um valor. Ele retorna uma promessa que executará um ``setTimeout` pelo número de milissegundos que devemos esperar.
featured image - Tudo o que você precisa saber sobre promessas, possibilidades e avaliação preguiçosa
Austin Gil HackerNoon profile picture

É o começo de um novo ano e, embora muitas pessoas prometam ser mais ativas, vou mostrar a você como fazer Promise s para ser mais preguiçoso… JavaScript Promise , isto é.


Fará mais sentido daqui a pouco.

Primeiro, vamos ver um exemplo básico de Promise . Aqui, tenho uma função chamada sleep que leva um tempo em milissegundos e um valor. Ele retorna uma promessa que executará um setTimeout pelo número de milissegundos que devemos esperar; então a promessa resolve com o valor.


 /** * @template ValueType * @param {number} ms * @param {ValueType} value * @returns {Promise<ValueType>} */ function sleep(ms, value) { return new Promise((resolve) => { setTimeout(() => resolve(value), ms); }); }


Funciona assim:


Console JavaScript com o código "await sleep(1000, 'Yawn & stretch')". Então, após um segundo, "'Yawn & stretch'"


Podemos aguardar a função sleep com os argumentos 1000 e 'Yawn & stretch' e, após um segundo, o console registrará a string 'Yawn & stretch'.


Não há nada de muito especial nisso. Ele provavelmente se comporta como você esperaria, mas fica um pouco estranho se o armazenarmos como uma variável para await mais tarde, em vez de await o Promise retornado imediatamente.


 const nap = sleep(1000, 'Yawn & stretch')


Agora, digamos que fazemos algum outro trabalho que leva tempo (como digitar o próximo exemplo) e, em seguida, await a variável nap .


Digitando no console JavaScript, "await nap" e vendo imediatamente a resposta "'Yawn & stretch'"

Você pode esperar um atraso de um segundo antes de resolver, mas, na verdade, ele resolve imediatamente. Sempre que você cria um Promise , você instancia qualquer funcionalidade assíncrona pela qual ele é responsável.


Em nosso exemplo, no momento em que definimos a variável nap , é criada a Promise que executa o setTimeout . Como sou um digitador lento, a Promise será resolvida no momento em que a await .


Em outras palavras, Promise s estão ansiosos. Eles não await que você os espere.


Em alguns casos, isso é bom. Em outros casos, pode levar ao uso desnecessário de recursos. Para esses cenários, você pode querer algo que se pareça com um Promise , mas use avaliação preguiçosa para instanciar apenas quando você precisar.


Antes de continuarmos, quero mostrar-lhe algo interessante.


As Promise não são as únicas coisas que podem ser await em JavaScript. Se criarmos um Object simples com um método .then() , podemos await esse objeto como qualquer Promise .


Console JavaScript com o texto "await { then: () => console.log('🙃') }" seguido por "🙃".

Isso é meio estranho, mas também nos permite criar diferentes objetos que se parecem com Promise s, mas não são. Esses objetos às vezes são chamados de “ Thenables “.


Com isso em mente, vamos criar um novo classe chamado LazyPromise que estende o construtor interno de Promise . Estender Promise não é estritamente necessário, mas faz com que pareça mais semelhante a uma Promise usando coisas como instanceof .


 class LazyPromise extends Promise { /** @param {ConstructorParameters<PromiseConstructor>[0]} executor */ constructor(executor) { super(executor); if (typeof executor !== 'function') { throw new TypeError(`LazyPromise executor is not a function`); } this._executor = executor; } then() { this.promise = this.promise || new Promise(this._executor); return this.promise.then.apply(this.promise, arguments); } }


A parte a ser focada é o método then() . Ele sequestra o comportamento padrão de um Promise padrão para esperar até que o método .then() seja executado antes de criar um Promise real.


Isso evita instanciar a funcionalidade assíncrona até que você realmente a chame. E funciona se você chamar explicitamente .then() ou usar await .


Agora, vamos ver o que acontece se substituirmos o Promise na função sleep original por um LazyPromise . Mais uma vez, atribuiremos o resultado a uma variável nap .


 function sleep(ms, value) { return new LazyPromise((resolve) => { setTimeout(() => resolve(value), ms); }); } const nap = sleep(1000, 'Yawn & stretch')


Em seguida, tomamos nosso tempo para digitar a linha await nap e executá-la.


Digitando no console JavaScript, "await nap" e após um atraso de um segundo, vendo a resposta "'Yawn & stretch'"


Desta vez, vemos um atraso de um segundo antes que a Promise seja resolvida, independentemente de quanto tempo passou desde que a variável foi criada.


(Observe que esta implementação apenas cria o novo Promise uma vez e faz referência a ele em chamadas subsequentes. Portanto, se await novamente, ele será resolvido imediatamente como qualquer Promise normal )


Claro, este é um exemplo trivial que você provavelmente não encontrará no código de produção, mas há muitos projetos que usam objetos do tipo Promise avaliados preguiçosamente. Provavelmente, o exemplo mais comum é com bancos de dados ORM s e construtores de consulta como Knex.js ou Prisma .


Considere o pseudo-código abaixo. É inspirado por alguns destes construtores de consulta:


 const query = db('user') .select('name') .limit(10) const users = await query


Criamos uma consulta de banco de dados que vai até a tabela "user" e seleciona as dez primeiras entradas e retorna seus nomes. Em teoria, isso funcionaria bem com um Promise regular.


Mas e se quiséssemos modificar a consulta com base em certas condições, como parâmetros de string de consulta? Seria bom poder continuar modificando a consulta antes de finalmente aguardar o Promise .


 const query = db('user') .select('name') .limit(10) if (orderBy) { query.orderBy(orderBy) } if (limit) { query.limit(limit) } if (id) { query.where({ id: id }) } const users = await query


Se a consulta de banco de dados original fosse uma Promise padrão, ela instanciaria prontamente a consulta assim que atribuíssemos a variável e não poderíamos modificá-la posteriormente.


Com a avaliação preguiçosa, podemos escrever um código como este que é mais fácil de seguir, melhora a experiência do desenvolvedor e só executa a consulta uma vez quando precisamos dela.


Esse é um exemplo em que a avaliação preguiçosa é ótima. Também pode ser útil para coisas como construir, modificar e orquestrar solicitações HTTP.


Lazy Promise s são muito legais para os casos de uso certos, mas isso não quer dizer que devam substituir todos os Promise . Em alguns casos, é bom instanciar rapidamente e ter a resposta pronta o mais rápido possível.


Este é outro daqueles cenários “depende”. Mas da próxima vez que alguém lhe pedir para fazer uma Promise , considere ser preguiçoso ( ͡° ͜ʖ ͡°).


Muito obrigado pela leitura. Se você gostou deste artigo, por favor Compartilhe . É uma das melhores formas de me apoiar. Você também pode assine minha newsletter ou Siga me no twitter se você quiser saber quando novos artigos são publicados.


Originalmente publicado em austingil.com .