- Notifications
You must be signed in to change notification settings - Fork111
Introduction callbacks#133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:master
Are you sure you want to change the base?
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
3b7521d91547eedc9cc800aa5cb2094345c110d4fef954d7a89747adced5816File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,67 +1,67 @@ | ||
| #Introdução: callbacks | ||
| ```warn header="Nós usamos métodos do navegador aqui nos exemplos" | ||
| Para demonstrar o uso de callbacks, promisese outros conceitos abstratos, nós vamos usar alguns métodos do navegador: especificamente, carregar scriptse fazer manipulações simples de documentos. | ||
| Se você não está familiarizado com esses métodos, e o uso deles nos exemplos parece confuso, pode ser que você queira ler alguns capítulos da [próxima parte](/document)do tutorial. | ||
| Mas nós vamos tentar deixar as coisas claras de qualquer jeito. Não vai ter nada muito complexo em relação ao navegador. | ||
| ``` | ||
| Muitas funções providas peloJavaScriptpermitem que você agende ações *assíncronas*. Em outras palavras, ações que nós iniciamos agora, mas que terminam mais tarde. | ||
| Por exemplo, uma dessas funções é a função`setTimeout`. | ||
| Existem outros exemplos práticos de ações assíncronas, por exemplo: carregarscriptse módulos (nós vamos ver nos capítulos adiante). | ||
| Veja afunção`loadScript(src)`,que carrega um scriptcom um dado `src`: | ||
| ```js | ||
| function loadScript(src) { | ||
| //cria uma tag<script>e a anexa à página | ||
| //isso faz com que oscriptcom o determinadosrccomece a carregar e seja executado quando concluído | ||
| let script = document.createElement('script'); | ||
| script.src = src; | ||
| document.head.append(script); | ||
| } | ||
nildo marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| ``` | ||
| Ela acrescenta ao documento uma nova, dinamicamente criada, tag `<script src="…">`com o`src` passado. O navegador começa a carregar ele automaticamente e o executa quando terminar. | ||
| Podemos usar essa função assim: | ||
| ```js | ||
| //carrega e executa o scriptno caminho dado | ||
| loadScript('/my/script.js'); | ||
| ``` | ||
| O scripté executado "assincronamente",porque ele começa a carregar agora, mas executa mais tarde, quando a função já terminou. | ||
| Se tiver algum código abaixo de`loadScript(…)`,ele não espera até que oscripttermine de carregar. | ||
| ```js | ||
| loadScript('/my/script.js'); | ||
| //o código embaixo deloadScriptnão espera o carregamento doscriptterminar | ||
| // ... | ||
| ``` | ||
| Agora, vamos imaginar que queremos usar o novo scriptassim que ele terminar de carregar. Ele provavelmente declara novas funções, e queremos executar elas. | ||
| Mas se nós fizéssemos isso imediatamente depois da chamada`loadScript(…)`, não iria funcionar. | ||
| ```js | ||
| loadScript('/my/script.js'); //o scripttem "function newFunction() {…}" | ||
| *!* | ||
| newFunction(); //a função não existe! | ||
| */!* | ||
| ``` | ||
| Naturalmente, o navegador provavelmente não teve tempo de carregar o script.Então a chamada imediata para a nova função falha. Do jeito que está, a função`loadScript`não provê uma maneira de saber quando o carregamento termina. O scriptcarrega e eventualmente é executado, isso é tudo. Mas nós queremos saber quando isso acontece, para podermos usar as novas funções e variáveis daquele script. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Sugestão de alteração:
| ||
| Vamos adicionar uma função`callback`como segundo argumento em`loadScript`que deve executar quando o scriptterminar de carregar: | ||
| ```js | ||
| function loadScript(src, *!*callback*/!*) { | ||
| @@ -76,19 +76,19 @@ function loadScript(src, *!*callback*/!*) { | ||
| } | ||
| ``` | ||
| Agora se nós quisermos chamar as novas funções doscript,nós podemos fazer isso no callback: | ||
| ```js | ||
| loadScript('/my/script.js', function() { | ||
| //o callbackexecuta depois que oscripttermina de carregar | ||
| newFunction(); //então agora funciona | ||
| ... | ||
| }); | ||
| ``` | ||
| Esta é a ideia: o segundo argumento é uma função (normalmente anônima) que executa quando a ação termina. | ||
| Veja um exemplo executável com umscript real: | ||
| ```js run | ||
| function loadScript(src, callback) { | ||
| @@ -100,39 +100,39 @@ function loadScript(src, callback) { | ||
| *!* | ||
| loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => { | ||
| alert(`Legal, o script ${script.src}está carregado`); | ||
| alert( _ ); //função declarada noscript carregado | ||
| }); | ||
| */!* | ||
| ``` | ||
| Isso é chamado de programação assíncrona "baseada em callbacks". Afunção que faz alguma coisa assincronamente deve prover um argumento`callback`onde nós colocamos a função que vai executar depois que ela estiver completa. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Sugestão de alteração:
| ||
| Aqui nós fizemos isso em `loadScript`,mas é claro que isso é uma abordagem genérica. | ||
| ## Callbackno callback | ||
| Como poderíamos carregar doisscriptssequencialmente? Carregar um primeiro, e depois o segundo? | ||
| A soluçãonaturalseria colocar a segunda chamada de`loadScript`dentro docallback,assim: | ||
| ```js | ||
| loadScript('/my/script.js', function(script) { | ||
| alert(`Legal,${script.src}foi carregado, vamos carregar mais um`); | ||
| *!* | ||
| loadScript('/my/script2.js', function(script) { | ||
| alert(`Legal, o segundo scriptfoi carregado`); | ||
| }); | ||
| */!* | ||
| }); | ||
| ``` | ||
| Depois que o `loadScript`de fora termina, o callbackinicia o `loadScript` de dentro. | ||
| E se nós quisermos mais um script...? | ||
| ```js | ||
| loadScript('/my/script.js', function(script) { | ||
| @@ -141,7 +141,7 @@ loadScript('/my/script.js', function(script) { | ||
| *!* | ||
| loadScript('/my/script3.js', function(script) { | ||
| // ...continua depois que todo osscriptsforem carregados | ||
| }); | ||
| */!* | ||
| @@ -150,13 +150,13 @@ loadScript('/my/script.js', function(script) { | ||
| }); | ||
| ``` | ||
| Então, toda ação nova fica dentro de umcallback.Tudo bem para poucas ações, mas não é bom para muitas ações. Por isso nós vamos ver outras variantes em breve. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Sugestão de alteração:
| ||
| ##Tratando erros | ||
| No exemplo acima nós não consideramos erros. E se o carregamento do script falhar? Nosso callbackdeveria ser capaz de reagir a isso. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Sugestão de alteração:
| ||
| Abaixo temos uma versão melhorada do`loadScript`que rastreia os erros de carregamento: | ||
| ```js | ||
| function loadScript(src, callback) { | ||
| @@ -165,39 +165,39 @@ function loadScript(src, callback) { | ||
| *!* | ||
| script.onload = () => callback(null, script); | ||
| script.onerror = () => callback(new Error(`Erro no carregamento do script ${src}`)); | ||
| */!* | ||
| document.head.append(script); | ||
| } | ||
| ``` | ||
| O código acima chama`callback(null, script)`quando o carregamento é feito com sucesso e`callback(error)`caso contrário. | ||
| Usando a função: | ||
| ```js | ||
| loadScript('/my/script.js', function(error, script) { | ||
| if (error) { | ||
| //tratar o erro | ||
| } else { | ||
| // scriptcarregado com sucesso | ||
| } | ||
| }); | ||
| ``` | ||
| De novo, o padrão que nós usamos para o`loadScript`é bem comum. É chamado de estilo "callback com erro primeiro". | ||
| A convenção é: | ||
| 1.O primeiro argumento do`callback`é reservado para um erro, se algum ocorrer. Então `callback(err)`é chamado. | ||
| 2.O segundo argumento (e o próximo se for necessário) são para quando houver sucesso. Então `callback(null, result1, result2…)`é chamado. | ||
| Assim uma única função`callback`é usada tanto para reportar erros quanto para retornar os resultados. | ||
| ##Pirâmide da Perdição | ||
| À primeira vista parece uma maneira viável de programação assíncrona. E realmente é. Para uma ou duas chamadas aninhadas está ok. | ||
| Mas para múltiplas ações assíncronas que seguem uma depois da outra, vamos ter um código como esse: | ||
| ```js | ||
| loadScript('1.js', function(error, script) { | ||
| @@ -216,7 +216,7 @@ loadScript('1.js', function(error, script) { | ||
| handleError(error); | ||
| } else { | ||
| *!* | ||
| // ...continua depois que todos osscriptssão carregados (*) | ||
| */!* | ||
| } | ||
| }); | ||
| @@ -227,14 +227,14 @@ loadScript('1.js', function(error, script) { | ||
| }); | ||
| ``` | ||
| No código acima: | ||
| 1.Carregamos`1.js`,e depois, se não tiver nenhum erro... | ||
| 2.Carregamos`2.js`,e depois, se não tiver nenhum erro... | ||
| 3.Carregamos`3.js`,e depois, se não tiver nenhum erro--faz outra coisa `(*)`. | ||
| À medida em que as chamadas ficam mais aninhadas, o código vai ficando mais profundo e cada vez mais difícil de gerenciar, especialmente se nós tivermos um códigorealem vez de `...`, que pode incluir mais laços, condicionais e assim por diante. | ||
| Isso é às vezes chamado de"callback hell (inferno dos callbacks)" ou "pyramid of doom (pirâmide da perdição)." | ||
| <!-- | ||
| loadScript('1.js', function(error, script) { | ||
| @@ -262,11 +262,11 @@ loadScript('1.js', function(error, script) { | ||
|  | ||
| A "pirâmide" de chamadas aninhadas cresce para a direita a cada ação assíncrona e rapidamente sai do controle. | ||
| Então esse jeito de programar não é muito bom. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Sugestão de alteração:
| ||
| Nós podemos tentar diminuir o problema fazendo cada ação ser uma função separada, assim: | ||
| ```js | ||
| loadScript('1.js', step1); | ||
| @@ -293,17 +293,17 @@ function step3(error, script) { | ||
| if (error) { | ||
| handleError(error); | ||
| } else { | ||
| // ...continua depois que todos osscriptssão carregados (*) | ||
| } | ||
| } | ||
| ``` | ||
| Viu? Isso faz a mesma coisa, e não tem aninhamento profundo agora porque nós transformamos cada ação em uma função de nível superior separada. | ||
| Funciona, porém o código parece uma planilha dividida. É difícil de ler, e você provavelmente percebeu que precisamos de pular entre as partes do código enquanto estamos lendo ele. Isso é inconveniente, especialmente se o leitor não estiver familiarizado com o código e não souber para onde pular. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Sugestão de alteração:
| ||
| Além disso, as funções chamadas `step*`são todas utilizadas apenas uma vez. Elas são criadas apenas pra evitar a "pirâmide da perdição."Ninguém vai reutilizá-las fora da cadeia de ações. Então tem um pouco de bagunça aqui. | ||
| Gostaríamos de ter algo melhor. | ||
| Felizmente, existem outras maneiras de evitar essas pirâmides. Uma das melhores maneiras é usar"promises (promessas)",descritas no próximo capítulo. | ||





