Skip to content

Instantly share code, notes, and snippets.

@igorlima igorlima/Readme.md
Last active Dec 31, 2015

Embed
What would you like to do?
JavaScript de forma assíncrona e legível

Gittip Donate Button

A programação assíncrona possui a vantagem de gerar códigos perfomáticos. Em certos casos, a implementação de diversas funções assíncronas encadeadas através de funções callback pode prejudicar a leitura e a manutenção do código. Para demonstrar esse encadeamento, vamos utilizar um trecho de código que utiliza a API do Selenium 2. Baseado em um exemplo do site do SauceLabs.

A API do Selenium WebDriver pode ser utilizada por diversas linguagem de programação, porém, em nosso exemplo, iremos utilizar o NodeJS (JavaScript) e o gerenciador de pacotes NPM, que podem ser baixados no site oficial. O NPM é necessário para instalar o PhantomJS e o WD, utilizando o seguinte script:

npm install -g phantomjs

npm install wd

O NodeJS possui dois tipos de dependências: global ou local. Quando uma dependência é global, o pacote passa a ser executável, tornando possível a utilização da dependência através da linha de comando. Já as dependências locais são instaladas no diretório corrente, dentro de node_modules.

O primeiro script, utilizando o parâmetro -g, instala o PhantomJS como dependência global, que é um WebKit headless totalmente em JavaScript e possui suporte rápido e nativo para vários padrões web como manipulação de DOM, seletores CSS, JSON, Canvas e SVG.

Já o segundo script, instala como dependência local o WD, que é um cliente NodeJS para facilitar o acesso à API do Selenium 2 e suporta métodos como: fazer requisições GET e POST, clicar no botão VOLTAR do navegador, fazer refresh no navegador, pegar um printscreen da tela atual, redimensionar e mover a janela do navegador, submeter formulário, digitar texto, usar cookies, selecionar um elemento DOM, clicar e mover um elemento, etc.

Após a instalação das dependências, vamos criar um arquivo com vários callbacks encadeados. Esse código possui seis passos: (i) abrir o navegador, (ii) acessar uma página de teste, (iii) verificar o título da página, (iv) submeter um formulário, (v) verificar a url da página após enviar o formulário e (vi) fechar o navegador. Algo bem simples. Suficiente para ressaltar a quantidade de callbacks encadeados.

Um detalhe importante sobre assincronismo é que, na maioria dos casos, os callbacks possuem como parâmetro uma variável de erro, que serve para impedir a execução dos callbacks subsequentes, caso haja algum problema.

Criado o arquivo exemplo, é preciso, em um outro terminal, rodar o PhantomJS em modo WebDriver, digitando o seguinte comando:

phantomjs --webdriver=localhost:8910

Com o PhantomJS rodando em segundo plano, execute o exemplo usando o node. Segue o comando e uma ilustração do resultado obtido:

node um-exemplo-COM-varios-callbacks-encadeados.js

ilustração do resultado obtido após executar o exemplo COM vários callbacks encadeados

Para evitar tantos callbacks encadeados, vamos utilizar a biblioteca Async que prover várias funções que facilitam a programação assíncrona em JavaScript. Nesse exemplo usaremos a função waterfall. Uma alternativa mais leve para código Front-End é a biblioteca Underscore. Para instalar o Async, utilize o seguinte script:

npm install async

Agora vamos criar um outro arquivo sem tantos encadeamentos e executá-lo. Lembre-se que o PhantomJS ainda deve estar rodando em segundo plano. Segue o comando e uma ilustração do resultado obtido:

node um-exemplo-SEM-varios-callbacks-encadeados.js

ilustração do resultado obtido após executar o exemplo SEM vários callbacks encadeados

Esse trecho de código exemplifica como vários callbacks encadeados podem ser evitados com o uso de uma estrutura de controle. Muito obrigado.

var webdriver = require('wd'),
assert = require('assert'),
browser = webdriver.remote({
hostname: "localhost",
port: 8910
});
browser.init({}, function(erro, id_da_sessao, recursos_webdriver) {
console.log('navegador aberto');
browser.get("http://saucelabs.com/test/guinea-pig", function(erro) {
console.log('pagina de teste aberta');
browser.title(function(erro, title) {
console.log('verificando titulo da pagina...');
assert.ok( title.indexOf('I am a page title - Sauce Labs')===0, 'titulo NAO esta correto');
browser.elementById('submit', function(erro, elemento) {
console.log('botao enviar encontrado');
browser.clickElement(elemento, function(erro) {
console.log('botao clicado');
browser.eval("window.location.href", function(erro, href) {
console.log('verificando url da pagina...');
assert.ok(href.indexOf('guinea')>0, 'pagina NAO esta correta');
browser.quit(function(erro){
console.log('navegador fechado');
});
});
});
});
});
});
});
var webdriver = require('wd'),
async = require('async'),
assert = require('assert'),
browser = webdriver.remote({
hostname: "localhost",
port: 8910
});
async.waterfall([
function(callback_navegador_aberto) {
browser.init({}, callback_navegador_aberto);
},
function(id_da_sessao, recursos_webdriver, callback_pagina_aberta) {
console.log('navegador aberto');
browser.get("http://saucelabs.com/test/guinea-pig", callback_pagina_aberta);
},
function(callback_titulo) {
console.log('pagina de teste aberta');
browser.title(callback_titulo);
},
function(title, callback_elemento_encontrado) {
console.log('verificando titulo da pagina...');
assert.ok( title.indexOf('I am a page title - Sauce Labs')===0, 'titulo NAO esta correto');
browser.elementById('submit', callback_elemento_encontrado);
},
function(elemento, callback_botao_clicado) {
console.log('botao enviar encontrado');
browser.clickElement(elemento, callback_botao_clicado);
},
function(callback_verificar_url) {
console.log('botao clicado');
browser.eval("window.location.href", callback_verificar_url);
},
function(href, callback_navegador_fechado) {
console.log('verificando url da pagina...');
assert.ok(href.indexOf('guinea')>0, 'pagina NAO esta correta');
browser.quit(callback_navegador_fechado);
},
function(callback_final) {
console.log('navegador fechado');
callback_final();
}
], function(erro){
erro && console.log('algum erro ocorreu', erro);
});
@ronaldoveras

This comment has been minimized.

Copy link

commented Jan 16, 2014

Muito bom. Deixa o código bem mais legível. Tutorial massa.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.