Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
May 11, 2021 01:35 am GMT

Programao assncrona

Quando fazemos uma ligao telefnica para uma pessoa para passar uma mensagem, dependemos de outra ao que a da pessoa atender a chamada. Vamos tentar representar isso em cdigo utilizando a linguagem JavaScript:

function ligacao() {    console.log("eu fao a chamada");    console.log("a pessoa atende e diz al");    console.log("eu digo alguma informao"); }ligacao();

A sada ser:

eu fao a chamadaa pessoa atende e diz aleu digo alguma informao

Callbacks

Na realidade, a pessoa no atende a mensagem imediatamente, ela pode demorar alguns segundos para atender. Podemos representar essa "demora" atravs da funo setTimeout() que executa uma funo aps determinado espao de tempo. Ela recebe dois argumentos - o primeiro a funo que representa a ao a ser executada e o segundo o valor em milisegundos representando o tempo mnimo de espera para que ela seja executada:

setTimeout(() => {    console.log("a pessoa atende e diz al")}, 3000);

Como resultado, aps 3 segundos, temos:

a pessoa atende e diz al

Agora vamos utilizar este recurso no nosso exemplo:

function ligacao() {    console.log("eu fao a chamada");    setTimeout(() => {        console.log("a pessoa atende e diz al")    }, 3000);    console.log("eu digo alguma informao"); }

sada:

eu fao a chamadaeu digo alguma informaoa pessoa atende e diz al

Note que nosso programa apresenta alguns problemas. A pessoa que fez a chamada (no caso, eu) acaba dizendo alguma coisa antes da outra pessoa antender. Ou seja, a execuo no aconteceu de maneira sncrona, mantendo a ordenao esperada. O contedo dentro de setTimeout() no foi executado aps a primeira chamada de console.log().

Isso acontece porque o Javascript single-thread. O que quer dizer, a grosso modo, que possui uma stack principal de execuo do programa e executa um comando por vez, do incio ao fim, sem interrupes. No momento em que cada operao processada, nada mais pode acontecer.

Acabamos de ver que seu funcionamento diferente quando o programa encontra a funo setTimeout(). O mtodo setTimeout pertence ao mdulo timers que contm funes que executam algum cdigo aps um determinado perodo de tempo. No necessrio importar este mdulo no node.js j que todos estes mtodos esto disponveis globalmente para simular o JavaScript Runtime Environment dos navegadores.

A chamada da funo que passamos como primeiro argumento para o setTimeout() enviada para outro contexto, chamado WEBApi que define um timer com o valor que passamos como segundo argumento (3000) e arguarda este tempo para colocar a chamada da funo na stack principal para ser executada - ocorre um agendamento desta execuo. Porm, este agendamento s concretizado aps a stack principal ser limpa, ou seja, aps todo cdigo sncrono ser executado. Por este motivo, a terceira e ltima chamada de console.log chamada antes da segunda.

A funo que passamos como primeiro argumento para o mtodo setTimeout() chamada de funo callback. Uma funo callback toda funo passada como argumento para outra funo que de fato vai execut-la. Esta execuo pode ser imediata, ou seja, executada de maneira sncrona. No entando, callbacks so normalmente utilizados para continuar a execuo de um cdigo em outro momento na linha do tempo, ou seja, de maneira assncrona. Isso bastante til quando temos eventos demorados e no queremos travar o restante do programa.

Nosso cdigo ainda tem problemas. A pessoa que faz a ligao quer apenas dizer alguma coisa aps a outra pessoa atender a chamada. Podemos refatorar o cdigo da seguinte maneira:

function fazChamada(){    console.log("eu fao a chamada");}function pessoaAtende() {    setTimeout(() => {        console.log("a pessoa atende e diz al")    }, 3000);}function euDigoAlgo() {    setTimeout(() => {        console.log("eu digo alguma informao");    }, 5000);}function ligacao() {    fazChamada();    pessoaAtende();    euDigoAlgo();}ligacao();

Podemos definir um tempo de espera maior para dizer algo na chamada, mas ainda assim no sabemos ao certo o quanto a pessoa vai demorar para atender. Se ela atender imediatamente, vai demorar para receber a mensagem e desligar a chamada sem que isso acontea. Alm de ser bastante ruim e trabalhoso ficar configurando os tempos de cada execuo, o cdigo fica muito grande e confuso com muitas condicionais.

Promises

Para nossa sorte, o Javascript possui um recurso chamado Promise que representa, como seu nome sugere, uma promessa de algo que ser executado futuramente. Como a execuo que esperamos pode falhar, este recurso tambm ajuda muito nos tratamentos de erros.

Segundo o Wikipdia, um Promise atua como representante de um resultado que , inicialmente, desconhecido devido a sua computao no estar completa no momento de sua chamada. Vamos contruir um objeto Promise para entender como ele funciona:

var p = new Promise();console.log(p);

Isso vai gerar um TypeError com a mensagem "TypeError: Promise resolver is not a function". Um objeto Promise precisa receber uma funo para resolver um valor. Ou seja, precisamos passar uma funo callback para executar algo:

var p = new Promise(() => console.log(5));

Este cdigo imprime o valor 5. Agora vamos imprimir o prprio objeto Promise:

var p = new Promise(() => console.log(5));console.log(p);

Sada:

5Promise { <pending> }

Note que executou o callback, mas seu estado est pendente. Toda vez que criamos um objeto Promise, seu estado inicial pendente j que representa a promessa de algo que ser resolvido no futuro. Neste caso, como o callback ser executado de maneira sncrona, vai imprimir o resultado de sua execuo. E, portanto, no til neste caso especfico.

Pode acontecer do callback executar o processamento de um valor que ser necessrio no futuro. Para que este valor esteja disponvel, ser preciso que a promessa seja resolvida atravs da funo annima resolve() que cria uma nova promessa com o valor realizado. Exemplo:

var p = new Promise((resolve) => {    resolve(5);});console.log(p);

Sada:

Promise { 5 }

Agora a promessa no est mais pendente, ela foi resolvida e embrulha o valor 5. Isso quer dizer que tudo deu certo. Porm, ainda uma promessa. Para imprimir o valor, precisamos utilizar o mtodo then() que anexa callbacks para a resoluo:

var p = new Promise((resolve) => {    resolve(5);});p.then(value => console.log(value));

Mas um erro pode acontecer quando a promessa tentar resolver um valor:

var p = new Promise((resolve) => {    try {        throw new Error("algo de errado ocorreu");        resolve(5);    } catch(err) {        return err;    }});console.log(p);p.then(v => console.log(v))

A promessa est pendente, mas nada foi executado ao chamarmos then(v => console.log(v)). Isso acontece j que ela no foi resolvida por causa de um erro. Para sabermos qual erro ocorreu, precisamos passar outro callback que ser responsvel por tratar falhas quando a promessa de um resultado for rejeitada, chamado reject().

var p = new Promise((resolve, reject) => {    try {        throw new Error("algo de errado ocorreu");        resolve(5);    } catch(err) {        reject(err);    }});

E aps a chamada de then(), que s ser executado em caso de sucesso, chamamos o catch() que ser chamado em caso de erro:

var p = new Promise((resolve, reject) => {    try {        throw new Error("algo de errado ocorreu");        resolve(5);    } catch(err) {        reject(err);    }});// console.log(p);p.then(v => console.log(v)).catch(err => console.log(err.message));

O estado da promessa ser rejected e vai imprimir a mensagem de erro na execuo do catch().

Promises so bastante teis para chamadas assncronas, quando precisamos saber sobre os estados de execues futuras e tratar melhor as partes do cdigo que dependam dessa execuo.

Agora, vamos voltar ao nosso exemplo. Podemos utilizar Promises para melhorar o cdigo e fazer com que a pessoa que fez a chamada diga algo aps a outra pessoa atender a chamada:

function fazChamada(){    console.log("eu fao a chamada");}function depoisDe(delay) {    return new Promise((resolve, reject) => {        setTimeout(() => {            let atendeu = Math.random() > 0.5;            if(atendeu) {                resolve("al");            } else {                reject(new Error("a pessoa nao atendeu"));             }        }, delay);    });}function ligacao() {    fazChamada();    depoisDe(3000)        .then((msg) => console.log(`a pessoa atende e diz ${msg}`))        .then(() => console.log("eu digo alguma informao"))        .catch(err => console.log(err.message));}ligacao();

Async/Await

Nosso cdigo funciona e conseguimos representar uma chamada telefnica mais prxima da realidade. Porm, o cdigo da funo ligacao() possui uma chamada encadeada de vrias promessas - e poderia ser muito mais complexo do que isso, como muitas chamadas encadeadas de then(). Dependendo da complexidade dessas chamadas, pode ser um cdigo difcil de ler e entender. Um cdigo sncrono , na maioria dos casos, mais fcil de ler e entender.

Na especificao ES2017 foram introduzidas duas novas expresses - async e await - que deixam o trabalho com Promises mais confortvel para o desenvolvedor. A expresso async utilizada quando queremos criar funes assncronas. Quando posicionada antes da declarao de uma funo, quer dizer que essa funo retorna um objeto do tipo Promise:

async function retornaUm() {    return 1;}console.log(retornaUm());retornaUm().then(console.log);

Que vai gerar a sada:

Promise { 1 }1

Portanto, ao utilizar a expresso async em uma funo, seu retorno embrulhado em um objeto Promise. Agora que entendemos como funciona o async vamos ver como o await funciona.

O uso do await somente permitido em escopo de um funo async - deste modo, a palavra-chave async alm de embrulhar seu retorno em uma promessa, permite o uso do await. A palavra-chave await faz com que o JavaScript espere at que uma promessa seja resolvida (ou rejeitada) e retorne seu resultado.

async function retornaUm() {    return 1;}async function retornaDois() {    var num = await retornaUm();    return num + 1;}retornaDois().then(console.log)

A funo retornaDois() faz uma pausa na primeira linha e segue sua execuo quando a promessa retonraUm() resolvida. Portanto, espera a promessa ser finalizada. O mesmo acontece quando o valor rejeitado:

async function funcao() {    await Promise.reject(new Error("um erro ocorreu"));}funcao().catch(err => console.log(err.message));

E similar a:

async function funcao() {    await new Error("um erro ocorreu");}funcao().catch(err => console.log(err.message));

Como lana um erro, podemos fazer um tratamento com o bloco try/catch:

async function funcao() {    try {        await Promise.reject(new Error("um erro ocorreu"));    } catch(err) {        console.log(err.message);    }}funcao();

Note que o cdigo fica mais fcil de ler e raramente usamos as chamadas encadeadas de then() e catch(). Com a introduo de funes assncronas com async/await, a escrita de um cdigo assncrono fica parecido com a escrita de um cdigo sncrono.

Agora que aprendemos como funciona o async/await, podemos refatorar nosso cdigo para utilizar este recurso:

function fazChamada(){    console.log("eu fao a chamada");}function depoisDe(delay) {    return new Promise((resolve, reject) => {        setTimeout(() => {            const atendeu = Math.random() > 0.5;            if(atendeu) {                resolve("al");            } else {                reject(new Error("a pessoa nao atendeu"));             }        }, delay);    });}async function ligacao() {    fazChamada();    try {        const msg = await depoisDe(3000);        console.log(msg);    }catch(err) {        console.log(err.message);    }}ligacao();

Original Link: https://dev.to/thaisandre/programacao-assincrona-2g48

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To