Files
fastapi/docs/pt/docs/async.md
Nils Lindemann ebc8ac7295 📝 Tweak docs and translations links, typos, format (#11389)
Co-authored-by: SebastiĂĄn RamĂ­rez <tiangolo@gmail.com>
2024-04-18 14:53:19 -05:00

22 KiB

ConcorrĂȘncia e async / await

Detalhes sobre a sintaxe async def para funçÔes de operação de rota e alguns conceitos de cĂłdigo assĂ­ncrono, concorrĂȘncia e paralelismo.

Com pressa?

TL;DR:

Se vocĂȘ estiver utilizando bibliotecas de terceiros que dizem para vocĂȘ chamar as funçÔes com await, como:

results = await some_library()

Então, declare sua função de operação de rota com async def como:

@app.get('/')
async def read_results():
    results = await some_library()
    return results

!!! note VocĂȘ sĂł pode usar await dentro de funçÔes criadas com async def.


Se vocĂȘ estĂĄ usando biblioteca de terceiros que se comunica com alguma coisa (um banco de dados, uma API, sistema de arquivos etc) e nĂŁo tem suporte para utilizar await (esse Ă© atualmente o caso para a maioria das bibliotecas de banco de dados), entĂŁo declare suas funçÔes de operação de rota normalmente, com apenas def, como:

@app.get('/')
def results():
    results = some_library()
    return results

Se sua aplicação (de alguma forma) não tem que se comunicar com nada mais e tem que esperar que o respondam, use async def.


Se vocĂȘ simplesmente nĂŁo sabe, use apenas def.


Note: VocĂȘ pode misturar def e async def nas suas funçÔes de operação de rota tanto quanto necessĂĄrio e definir cada função usando a melhor opção para vocĂȘ. FastAPI irĂĄ fazer a coisa certa com elas.

De qualquer forma, em ambos os casos acima, FastAPI irĂĄ trabalhar assincronamente e ser extremamente rĂĄpido.

Seguindo os passos acima, ele serå capaz de fazer algumas otimizaçÔes de performance.

Detalhes Técnicos

VersÔes modernas de Python tem suporte para "código assíncrono" usando algo chamado "corrotinas", com sintaxe async e await.

Vamos ver aquela frase por partes na seção abaixo:

  • CĂłdigo assĂ­ncrono
  • async e await
  • Corrotinas

CĂłdigo assĂ­ncrono

CĂłdigo assĂ­ncrono apenas significa que a linguagem 💬 tem um jeito de dizer para o computador / programa đŸ€– que em certo ponto, ele đŸ€– terĂĄ que esperar por algo para finalizar em outro lugar. Vamos dizer que esse algo seja chamado "arquivo lento" 📝.

Então, durante esse tempo, o computador pode ir e fazer outro trabalho, enquanto o "arquivo lento" 📝 termine.

EntĂŁo o computador / programa đŸ€– irĂĄ voltar toda hora que tiver uma chance porquĂȘ ele ainda estĂĄ esperando o "arquivo lento", ou ele đŸ€– nunca irĂĄ terminar todo o trabalho que tem atĂ© esse ponto. E ele đŸ€– irĂĄ ver se alguma das tarefas que estava esperando jĂĄ terminaram, fazendo o que quer que tinham que fazer.

Depois, ele đŸ€– pega a primeira tarefa para finalizar (vamos dizer, nosso "arquivo lento" 📝) e continua o que ele tem que fazer com isso.

Esse "esperar por algo" normalmente se refere a operaçÔes I/O que são relativamente "lentas" (comparadas a velocidade do processador e da memória RAM), como esperar por:

  • dados do cliente para serem enviados atravĂ©s da rede
  • dados enviados pelo seu programa para serem recebidos pelo clente atravĂ©s da rede
  • conteĂșdo de um arquivo no disco pra ser lido pelo sistema e entregar ao seu programa
  • conteĂșdo que seu programa deu ao sistema para ser escrito no disco
  • uma operação remota API
  • uma operação no banco de dados para finalizar
  • uma solicitação no banco de dados esperando o retorno do resultado
  • etc.

Enquanto o tempo de execução é consumido mais pela espera das operaçÔes I/O, essas operaçÔes são chamadas de operaçÔes "limitadas por I/O".

Isso Ă© chamado de "assĂ­ncrono" porquĂȘ o computador / programa nĂŁo tem que ser "sincronizado" com a tarefa lenta, esperando pelo exato momento que a tarefa finalize, enquanto nĂŁo faz nada, para ser capaz de pegar o resultado da tarefa e dar continuidade ao trabalho.

Ao invés disso, sendo um sistema "assíncrono", uma vez finalizada, a tarefa pode esperar um pouco (alguns microssegundos) para que o computador / programa finalize o que quer que esteja fazendo,e então volte para pegar o resultado e continue trabalhando com ele.

Para "sĂ­ncrono" (contrĂĄrio de "assĂ­ncrono") tambĂ©m Ă© utilizado o termo "sequencial", porquĂȘ o computador / programa segue todos os passos, na sequĂȘncia, antes de trocar para uma tarefa diferente, mesmo se alguns passos envolvam esperar.

ConcorrĂȘncia e hambĂșrgueres

Essa idĂ©ia de cĂłdigo assĂ­ncrono descrito acima Ă© algo Ă s vezes chamado de "concorrĂȘncia". E Ă© diferente de "paralelismo".

ConcorrĂȘncia e paralelismo ambos sĂŁo relacionados a "diferentes coisas acontecendo mais ou menos ao mesmo tempo".

Mas os detalhes entre concorrĂȘncia e paralelismo sĂŁo bem diferentes.

Para ver essa diferença, imagine a seguinte histĂłria sobre hambĂșrgueres:

HambĂșrgueres concorrentes

VocĂȘ vai com seu crush 😍 na lanchonete, fica na fila enquanto o caixa pega os pedidos das pessoas na sua frente.

EntĂŁo chega a sua vez, vocĂȘ pede dois saborosos hambĂșrgueres para vocĂȘ e seu crush 😍.

VocĂȘ paga.

O caixa diz alguma coisa para o cara na cozinha para que ele tenha que preparar seus hambĂșrgueres (mesmo embora ele esteja preparando os lanches dos outros clientes).

O caixa te entrega seu nĂșmero de chamada.

Enquanto vocĂȘ espera, vocĂȘ vai com seu crush 😍 e pega uma mesa, senta e conversa com seu crush 😍 por um bom tempo (como seus hambĂșrgueres sĂŁo muito saborosos, leva um tempo para serem preparados).

Enquanto vocĂȘ estĂĄ sentado na mesa com seu crush 😍, esperando os hambĂșrgueres, vocĂȘ pode gastar o tempo admirando como lindo, maravilhoso e esperto Ă© seu crush 😍.

Enquanto espera e conversa com seu crush 😍, de tempos em tempos, vocĂȘ verifica o nĂșmero de chamada exibido no balcĂŁo para ver se jĂĄ Ă© sua vez.

EntĂŁo a certo ponto, Ă© finalmente sua vez. VocĂȘ vai no balcĂŁo, pega seus hambĂșrgueres e volta para a mesa.

VocĂȘ e seu crush 😍 comem os hambĂșrgueres e aproveitam o tempo.


Imagine que vocĂȘ seja o computador / programa nessa histĂłria.

Enquanto vocĂȘ estĂĄ na fila, tranquilo, esperando por sua vez, nĂŁo estĂĄ fazendo nada "produtivo". Mas a fila Ă© rĂĄpida porquĂȘ o caixa sĂł estĂĄ pegando os pedidos, entĂŁo estĂĄ tudo bem.

EntĂŁo, quando Ă© sua vez, vocĂȘ faz o trabalho "produtivo" de verdade, vocĂȘ processa o menu, decide o que quer, pega a escolha de seu crush 😍, paga, verifica se entregou o valor correto em dinheiro ou cartĂŁo de crĂ©dito, verifica se foi cobrado corretamente, verifica se seu pedido estĂĄ correto etc.

Mas entĂŁo, embora vocĂȘ ainda nĂŁo tenha os hambĂșrgueres, seu trabalho no caixa estĂĄ "pausado", porquĂȘ vocĂȘ tem que esperar seus hambĂșrgueres estarem prontos.

Mas enquanto vocĂȘ se afasta do balcĂŁo e senta na mesa com o nĂșmero da sua chamada, vocĂȘ pode trocar sua atenção para seu crush 😍, e "trabalhar" nisso. EntĂŁo vocĂȘ estĂĄ novamente fazendo algo muito "produtivo", como flertar com seu crush 😍.

EntĂŁo o caixa diz que "seus hambĂșrgueres estĂŁo prontos" colocando seu nĂșmero no balcĂŁo, mas vocĂȘ nĂŁo corre que nem um maluco imediatamente quando o nĂșmero exibido Ă© o seu. VocĂȘ sabe que ninguĂ©m irĂĄ roubar seus hambĂșrgueres porquĂȘ vocĂȘ tem o nĂșmero de chamada, e os outros tem os nĂșmeros deles.

EntĂŁo vocĂȘ espera que seu crush 😍 termine a histĂłria que estava contando (terminar o trabalho atual / tarefa sendo processada), sorri gentilmente e diz que vocĂȘ estĂĄ indo buscar os hambĂșrgueres.

EntĂŁo vocĂȘ vai no balcĂŁo, para a tarefa inicial que agora estĂĄ finalizada, pega os hambĂșrgueres, e leva para a mesa. Isso finaliza esse passo / tarefa da interação com o balcĂŁo. Agora Ă© criada uma nova tarefa, "comer hambĂșrgueres", mas a tarefa anterior, "pegar os hambĂșrgueres" jĂĄ estĂĄ finalizada.

HambĂșrgueres paralelos

VocĂȘ vai com seu crush 😍 em uma lanchonete paralela.

VocĂȘ fica na fila enquanto alguns (vamos dizer 8) caixas pegam os pedidos das pessoas na sua frente.

Todo mundo antes de vocĂȘ estĂĄ esperando pelos hambĂșrgueres estarem prontos antes de deixar o caixa porquĂȘ cada um dos 8 caixas vai e prepara o hambĂșrguer antes de pegar o prĂłximo pedido.

EntĂŁo Ă© finalmente sua vez, e pede 2 hambĂșrgueres muito saborosos para vocĂȘ e seu crush 😍.

VocĂȘ paga.

O caixa vai para a cozinha.

VocĂȘ espera, na frente do balcĂŁo, para que ninguĂ©m pegue seus hambĂșrgueres antes de vocĂȘ, jĂĄ que nĂŁo tem nĂșmeros de chamadas.

Enquanto vocĂȘ e seu crush 😍 estĂŁo ocupados nĂŁo permitindo que ninguĂ©m passe a frente e pegue seus hambĂșrgueres assim que estiverem prontos, vocĂȘ nĂŁo pode dar atenção ao seu crush 😍.

Isso Ă© trabalho "sĂ­ncrono", vocĂȘ estĂĄ "sincronizado" com o caixa / cozinheiro. VocĂȘ tem que esperar e estar lĂĄ no exato momento que o caixa / cozinheiro terminar os hambĂșrgueres e dĂĄ-los a vocĂȘ, ou entĂŁo, outro alguĂ©m pode pegĂĄ-los.

EntĂŁo seu caixa / cozinheiro finalmente volta com seus hambĂșrgueres, depois de um longo tempo esperando por eles em frente ao balcĂŁo.

VocĂȘ pega seus hambĂșrgueres e vai para a mesa com seu crush 😍.

VocĂȘs comem os hambĂșrgueres, e o trabalho estĂĄ terminado.

NĂŁo houve muita conversa ou flerte jĂĄ que a maior parte do tempo foi gasto esperando os lanches na frente do balcĂŁo.


Nesse cenĂĄrio dos hambĂșrgueres paralelos, vocĂȘ Ă© um computador / programa com dois processadores (vocĂȘ e seu crush 😍), ambos esperando e dedicando a atenção de estar "esperando no balcĂŁo" por um bom tempo.

A lanchonete paralela tem 8 processadores (caixas / cozinheiros). Enquanto a lanchonete dos hambĂșrgueres concorrentes tinham apenas 2 (um caixa e um cozinheiro).

Ainda assim, a Ășltima experiĂȘncia nĂŁo foi a melhor.


Essa poderia ser a histĂłria paralela equivalente aos hambĂșrgueres.

Para um exemplo "mais real", imagine um banco.

Até recentemente, a maioria dos bancos tinha muitos caixas e uma grande fila.

Todos os caixas fazendo todo o trabalho, um cliente apĂłs o outro.

E vocĂȘ tinha que esperar na fila por um longo tempo ou poderia perder a vez.

VocĂȘ provavelmente nĂŁo gostaria de levar seu crush 😍 com vocĂȘ para um rolezinho no banco.

ConclusĂŁo dos hambĂșrgueres

Nesse cenĂĄrio dos "hambĂșrgueres com seu crush 😍", como tem muita espera, faz mais sentido ter um sistema concorrente.

Esse é o caso da maioria das aplicaçÔes web.

Geralmente são muitos usuårios, e seu servidor estå esperando pelas suas conexÔes não tão boas para enviar as requisiçÔes.

E entĂŁo esperando novamente pelas respostas voltarem.

Essa "espera" Ă© medida em microssegundos, e ainda assim, somando tudo, Ă© um monte de espera no final.

Por isso que faz muito mais sentido utilizar cĂłdigo assĂ­ncrono para APIs web.

A maioria dos frameworks Python existentes mais populares (incluindo Flask e Django) foram criados antes que os novos recursos assíncronos existissem em Python. Então, os meios que eles podem ser colocados em produção para suportar execução paralela mais a forma antiga de execução assíncrona não são tão poderosos quanto as novas capacidades.

Mesmo embora a especificação principal para web assíncrono em Python (ASGI) foi desenvolvida no Django, para adicionar suporte para WebSockets.

Esse tipo de assincronicidade é o que fez NodeJS popular (embora NodeJS não seja paralelo) e que essa seja a força do Go como uma linguagem de programa.

E esse Ă© o mesmo nĂ­vel de performance que vocĂȘ tem com o FastAPI.

E como vocĂȘ pode ter paralelismo e sincronicidade ao mesmo tempo, vocĂȘ tem uma maior performance do que a maioria dos frameworks NodeJS testados e lado a lado com Go, que Ă© uma linguagem compilada prĂłxima ao C (tudo graças ao Starlette).

ConcorrĂȘncia Ă© melhor que paralelismo?

NĂŁo! Essa nĂŁo Ă© a moral da histĂłria.

ConcorrĂȘncia Ă© diferente de paralelismo. E Ă© melhor em cenĂĄrios especĂ­ficos que envolvam um monte de espera. Por isso, geralmente Ă© muito melhor do que paralelismo para desenvolvimento de aplicaçÔes web. Mas nĂŁo para tudo.

EntĂŁo, para equilibrar tudo, imagine a seguinte historinha:

VocĂȘ tem que limpar uma grande casa suja.

Sim, essa Ă© toda a histĂłria.


NĂŁo hĂĄ espera em lugar algum, apenas um monte de trabalho para ser feito, em mĂșltiplos cĂŽmodos da casa.

VocĂȘ poderia ter chamadas como no exemplo dos hambĂșrgueres, primeiro a sala de estar, entĂŁo a cozinha, mas vocĂȘ nĂŁo estĂĄ esperando por nada, apenas limpar e limpar, as chamadas nĂŁo afetariam em nada.

Levaria o mesmo tempo para finalizar com ou sem chamadas (concorrĂȘncia) e vocĂȘ teria feito o mesmo tanto de trabalho.

Mas nesse caso, se vocĂȘ trouxesse os 8 ex-caixas / cozinheiros / agora-faxineiros, e cada um deles (mais vocĂȘ) pudessem dividir a casa para limpĂĄ-la, vocĂȘs fariam toda a limpeza em paralelo, com a ajuda extra, e terminariam muito mais cedo.

Nesse cenĂĄrio, cada um dos faxineiros (incluindo vocĂȘ) poderia ser um processador, fazendo a sua parte do trabalho.

E a maior parte do tempo de execução é tomada por trabalho (ao invés de ficar esperando), e o trabalho em um computador é feito pela CPU, que podem gerar problemas que são chamados de "limite de CPU".


Exemplos comuns de limite de CPU sĂŁo coisas que exigem processamento matemĂĄtico complexo.

Por exemplo:

  • Processamento de ĂĄudio ou imagem

  • VisĂŁo do Computador: uma imagem Ă© composta por milhĂ”es de pixels, cada pixel tem 3 valores (cores, processamento que normalmente exige alguma computação em todos esses pixels ao mesmo tempo)

  • Machine Learning: Normalmente exige muita multiplicação de matrizes e vetores. Pense numa grande folha de papel com nĂșmeros e multiplicando todos eles juntos e ao mesmo tempo.

  • Deep Learning: Esse Ă© um subcampo do Machine Learning, entĂŁo o mesmo se aplica. A diferença Ă© que nĂŁo hĂĄ apenas uma grande folha de papel com nĂșmeros para multiplicar, mas um grande conjunto de folhas de papel, e em muitos casos, vocĂȘ utiliza um processador especial para construir e/ou usar modelos.

ConcorrĂȘncia + Paralelismo: Web + Machine learning

Com FastAPI vocĂȘ pode levar a vantagem da concorrĂȘncia que Ă© muito comum para desenvolvimento web (o mesmo atrativo de NodeJS).

Mas vocĂȘ tambĂ©m pode explorar os benefĂ­cios do paralelismo e multiprocessamento (tendo mĂșltiplos processadores rodando em paralelo) para trabalhos pesados que geram limite de CPU como aqueles em sistemas de Machine Learning.

Isso, mais o simples fato que Python é a principal linguagem para Data Science, Machine Learning e especialmente Deep Learning, faz do FastAPI uma ótima escolha para APIs web e aplicaçÔes com Data Science / Machine Learning (entre muitas outras).

Para ver como alcançar esse paralelismo em produção veja a seção sobre Deployment{.internal-link target=_blank}.

async e await

VersĂ”es modernas do Python tem um modo muito intuitivo para definir cĂłdigo assĂ­ncrono. Isso faz parecer normal o cĂłdigo "sequencial" e fazer o "esperar" para vocĂȘ nos momentos certos.

Quando tem uma operação que exigirĂĄ espera antes de dar os resultados e tem suporte para esses recursos Python, vocĂȘ pode escrever assim:

burgers = await get_burgers(2)

A chave aqui é o await. Ele diz ao Python que ele tem que esperar por get_burgers(2) para finalizar suas coisas antes de armazenar os resultados em burgers. Com isso, o Python saberå que ele pode ir e fazer outras coisas nesse meio tempo (como receber outra requisição).

Para o await funcionar, tem que estar dentro de uma função que suporte essa assincronicidade. Para fazer isso, apenas declare a função com async def:

async def get_burgers(number: int):
    # Fazer alguma coisa assĂ­ncrona para criar os hambĂșrgueres
    return burgers

...ao invés de def:

# Isso nĂŁo Ă© assĂ­ncrono
def get_sequential_burgers(number: int):
    # Faz alguma coisa sequencial para criar os hambĂșrgueres
    return burgers

Com async def, o Python sabe que, dentro dessa função, tem que estar ciente das expressÔes await, e que isso pode "pausar" a execução dessa função, e poderå fazer outra coisa antes de voltar.

Quando vocĂȘ quiser chamar uma função async def, vocĂȘ tem que "esperar". EntĂŁo, isso nĂŁo funcionarĂĄ:

# Isso nĂŁo irĂĄ funcionar, porquĂȘ get_burgers foi definido com: async def
burgers = get_burgers(2)

EntĂŁo, se vocĂȘ estĂĄ usando uma biblioteca que diz que vocĂȘ pode chamĂĄ-la com await, vocĂȘ precisa criar as funçÔes de operação de rota com async def, como em:

@app.get('/burgers')
async def read_burgers():
    burgers = await get_burgers(2)
    return burgers

Mais detalhes técnicos

VocĂȘ deve ter observado que await pode ser usado somente dentro de funçÔes definidas com async def.

Mas ao mesmo tempo, funçÔes definidas com async def tem que ser aguardadas. Então, funçÔes com async def pdem ser chamadas somente dentro de funçÔes definidas com async def também.

EntĂŁo, sobre o ovo e a galinha, como vocĂȘ chama a primeira função async?

Se vocĂȘ estivar trabalhando com FastAPI nĂŁo terĂĄ que se preocupar com isso, porquĂȘ essa "primeira" função serĂĄ a sua função de operação de rota, e o FastAPI saberĂĄ como fazer a coisa certa.

Mas se vocĂȘ quiser usar async / await sem FastAPI, verifique a documentação oficial Python.

Outras formas de cĂłdigo assĂ­ncrono

Esse estilo de usar async e await Ă© relativamente novo na linguagem.

Mas ele faz o trabalho com cĂłdigo assĂ­ncrono muito mais fĂĄcil.

Essa mesma sintaxe (ou quase a mesma) foi também incluída recentemente em versÔes modernas do JavaScript (no navegador e NodeJS).

Mas antes disso, controlar cĂłdigo assĂ­ncrono era bem mais complexo e difĂ­cil.

Nas versĂ”es anteriores do Python, vocĂȘ poderia utilizar threads ou Gevent. Mas o cĂłdigo Ă© um pouco mais complexo de entender, debugar, e pensar sobre.

Nas versĂ”es anteriores do NodeJS / Navegador JavaScript, vocĂȘ poderia utilizar "callbacks". O que leva ao inferno do callback.

Corrotinas

Corrotina é apenas um jeito bonitinho para a coisa que é retornada de uma função async def. O Python sabe que é uma função que pode começar e terminar em algum ponto, mas que pode ser pausada internamente também, sempre que tiver um await dentro dela.

Mas toda essa funcionalidade de cĂłdigo assĂ­ncrono com async e await Ă© muitas vezes resumida como "corrotina". É comparĂĄvel ao principal recurso chave do Go, a "Gorotina".

ConclusĂŁo

Vamos ver a mesma frase com o conteĂșdo cima:

VersÔes modernas do Python tem suporte para "código assíncrono" usando algo chamado "corrotinas", com sintaxe async e await.

Isso pode fazer mais sentido agora.

Tudo isso é o que deixa o FastAPI poderoso (através do Starlette) e que o faz ter uma performance impressionante.

Detalhes muito técnicos

!!! warning VocĂȘ pode provavelmente pular isso.

Esses são detalhes muito técnicos de como **FastAPI** funciona por baixo do capÎ.

Se vocĂȘ tem algum conhecimento tĂ©cnico (corrotinas, threads, blocking etc) e estĂĄ curioso sobre como o FastAPI controla o `async def` vs normal `def`, vĂĄ em frente.

FunçÔes de operação de rota

Quando vocĂȘ declara uma função de operação de rota com def normal ao invĂ©s de async def, ela Ă© rodada em uma threadpool externa que entĂŁo Ă© aguardada, ao invĂ©s de ser chamada diretamente (ela poderia bloquear o servidor).

Se vocĂȘ estĂĄ chegando de outro framework assĂ­ncrono que nĂŁo faz o trabalho descrito acima e vocĂȘ estĂĄ acostumado a definir triviais funçÔes de operação de rota com simples def para ter um mĂ­nimo ganho de performance (cerca de 100 nanosegundos), por favor observe que no FastAPI o efeito pode ser bem o oposto. Nesses casos, Ă© melhor usar async def a menos que suas funçÔes de operação de rota utilizem cĂłdigo que performem bloqueamento IO.

Ainda, em ambas as situaçÔes, as chances são que o FastAPI serå ainda mais råpido{.internal-link target=_blank} do que (ou ao menos comparåvel a) seus frameworks antecessores.

DependĂȘncias

O mesmo se aplica para as dependĂȘncias. Se uma dependĂȘncia tem as funçÔes com padrĂŁo def ao invĂ©s de async def, ela Ă© rodada no threadpool externo.

Sub-dependĂȘncias

VocĂȘ pode ter mĂșltiplas dependĂȘncias e sub-dependĂȘncias exigindo uma a outra (como parĂąmetros de definiçÔes de funçÔes), algumas delas podem ser criadas com async def e algumas com def normal. Isso ainda poderia funcionar, e aquelas criadas com def podem ser chamadas em uma thread externa ao invĂ©s de serem "aguardadas".

Outras funçÔes de utilidade

Qualquer outra função de utilidade que vocĂȘ chame diretamente pode ser criada com def normal ou async def e o FastAPI nĂŁo irĂĄ afetar o modo como vocĂȘ a chama.

Isso estĂĄ em contraste Ă s funçÔes que o FastAPI chama para vocĂȘ: funçÔes de operação de rota e dependĂȘncias.

Se sua função de utilidade Ă© uma função normal com def, ela serĂĄ chamada diretamente (como vocĂȘ a escreve no cĂłdigo), nĂŁo em uma threadpool, se a função Ă© criada com async def entĂŁo vocĂȘ deve esperar por essa função quando vocĂȘ chamĂĄ-la no seu cĂłdigo.


Novamente, esses sĂŁo detalhes muito tĂ©cnicos que provavelmente possam ser Ășteis caso vocĂȘ esteja procurando por eles.

Caso contrĂĄrio, vocĂȘ deve ficar bem com as dicas da seção acima: Com pressa?.