Conheça o CylonJS, um framework para robótica

O Cylon.js é um framework JavaScript para robótica, computação física e Internet das Coisas (IoT). Ele fornece uma maneira simples, mas poderosa, de criar soluções que incorporam vários dispositivos de hardware diferentes simultaneamente. Deseja usar o Node.js para robôs, drones e dispositivos de IoT? Este é o framework certo.

Instalação:

Você pode utilizar o npm para instalar o módulo cylon:

npm install cylon

No ubuntu, você pode utilizar o apt-get para instalar o NodeJs na sua máquina, e assim, utilizar o npm:

sudo apt-get install nodejs

Com o módulo core instalado, você precisa instalar módulos de suporte de hardware necessários. Utilizaremos os módulos firmata, gpio e i2c. Esses módulos são utilizados no Arduino para a ligação de um LED.

npm install cylon-firmata cylon-gpio cylon-i2c

Hello World com Cylon.js:

Para criar uma aplicação Olá Mundo com o Cylon.js, você precisará:

  • Fazer a requisição do módulo cylon;
  • Utilizar o método Robot para logar o resultado no console;
  • utilizar o método start() para mostrar o resultado.
let Cylon = require('cylon');

Cylon.robot({
    work: function() {
        every((1).second(), function() {
            console.log("Olá Mundo!");
        });
    }
}).start();

Com isso, temos um Olá Mundo!

Arduino = LED + Botão

Este exemplo acende um LED ao clicar em um botão:

let Cylon = require('cylon');

Cylon.robot({
    connections: {
        arduino: { adaptor: 'firmata', port: '/dev/ttyACM0'}
    },

    devices: {
        led: { driver: 'led', pin: 13 },
        button: { drier: 'button', pin: 2}
    },

    work: function(my){
        my.button.on('push', function() {
            my.led.toggle()
        });
    }
}).start();

O Cylon.js pode ser executado diretamente no navegador, usando o módulo NPM do browserify. Você pode executá-lo usando um aplicativo conectado ao Chrome ou um aplicativo móvel PhoneGap.

Você pode ver mais alguns exemplos do Cylon.JS em ação, consulte a página de exemplos.

O Cylon.js fornece uma maneira simples e poderosa de criar soluções que incorporam vários dispositivos de hardware diferentes ao mesmo tempo.

Para verificar atualizações, notas e outros comentários sobre o Cylon.js , você pode acessar o blog do Cylon.js. Também pode acessar o GitHub.

O Cylon.js foi feito e atualizado pelo Hybrid Groud. É licenciado sob a licença Apache 2.0.

Gostou deste artigo? Comente abaixo!

Criando e lendo arquivo com File System e NodeJS

O módulo File System do Node.js permite o trabalho com o sistema de arquivos do computador. Com ele, podemos criar, atualizar, ler e excluir diversos tipos de arquivos, como .txt, .html, entre outros. Neste exercício, criaremos um arquivo txt com um pequeno texto e realizar sua leitura. O conteúdo deverá ser exibido no shell do NodeJS. Vamos começar?

1º Passo:

Crie um arquivo JavaScript chamado writeFile.js. Neste arquivo você precisará:

  • Criar uma requisição para o módulo file system;
  • Criar uma variável texto que receberá o seguinte parágrafo: Este é o texto que usaremos como exemplo para salvar em nosso arquivo txt;
  • Utilizar o método writeFile para criar um arquivo chamado ‘newfile.txt’ e inserir neste arquivo o conteúdo da variável texto.

2º Passo:

Crie um arquivo JavaScript chamado readFile.js. Neste arquivo, você precisará:

  • Criar uma requisição para o módulo file system;
  • Utilizar o método readFile para leitura do conteúdo do arquivo ‘newfile.txt’. Não esqueça de adicionar o ‘utf-8’ para codificação dos caracteres;
  • Utilize o console.log para mostrar o conteúdo do nosso arquivo no terminal do NodeJS.

Sua saída deverá ser assim:

Você pode realizar o download do código fonte aqui:

[download id=”3594″]

Criando canal de mensagens com RabbitMQ e Node.js

Olá pessoal! Hoje irei mostrar como criar um canal de mensagens usando RabbitMQ e Node.js. Primeiramente, você deve tê-los instalado na sua máquina. Você pode realizar o download do RabbitMQ em: https://www.rabbitmq.com/download.html e do NodeJS em: https://nodejs.org/pt-br/download/.

Vamos começar?

Primeiramente, temos que ter em mente o conceito de fila. Caso não conheça a estrutura de dado fila, dê uma olhada neste artigo: https://www.mundojs.com.br/2019/11/01/estrutura-de-dados-com-javascript-fila/ para podermos dar sequência à este artigo.

No RabbitMQ, temos um produtor de mensagens (P) e um consumidor (C). As mensagens ficam armazenadas dentro de uma fila. No diagrama abaixo, a caixa do meio é a fila. O produtor de mensagens envia uma mensagem para a fila, e o consumidor a recebe:

Enviando:

Crie um arquivo send.js, que será o arquivo do produtor de mensagens. No nosso send.js, precisamos realizar a requisição da biblioteca amqplib. Isso pode ser feito desta maneira:

let amqp = require('amqplib/callback_api');

Em seguida, conectaremos ao servidor RabbitMQ:

amqp.connect('amqp://localhost', function(error0, connection) {
    if (error0)throw error0;
});

Agora, criaremos um canal onde reside a API para a realização das tarefas, onde devemos declarar uma fila para o envio, e também publicar uma mensagem na fila:

amqp.connect('amqp://localhost', function(error0, connection) {
    if (error0) throw error0;
    connection.createChannel(function(error1, channel) {
        if (error1) throw error1;
        
        let queue = 'hello';
        let msg = 'Olá Mundo!';

        channel.assertQueue(queue, {
            durable: false
        });
        channel.sendToQueue(queue, Buffer.from(msg));

        console.log(" [x] Enviando %s", msg);
    });
});

Agora, precisamos fechar a conexão e sair:

setTimeout(function() {
    connection.close();
    process.exit(0);
}, 500);

E o nosso código do produtor de mensagens seria mais ou menos assim:

let amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function(error0, connection) {
    if (error0) {
        throw error0;
    }
    connection.createChannel(function(error1, channel) {
        if (error1) throw error1;
        
        let queue = 'hello';
        let msg = 'Olá Mundo!';

        channel.assertQueue(queue, {
            durable: false
        });
        channel.sendToQueue(queue, Buffer.from(msg));

        console.log(" [x] Enviando %s", msg);
    });
    setTimeout(function() {
        connection.close();
        process.exit(0);
    }, 500);
});

Recebendo:

Agora, nosso consumidor precisa escutar as mensagens enviadas pelo arquivo send.js. O consumidor precisa ficar ativo para escutar as mensagens enviadas. Crie um arquivo chamado receive.js. Este arquivo terá a configuração parecida com o do arquivo send.js. Abrimos uma conexão, um canal e declaramos a fila que iremos consumir:

let amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function (error0, connection) {
    if (error0) throw error0;
    connection.createChannel(function (error1, channel) {
        if (error1) throw error1;
        let queue = 'hello';
        channel.assertQueue(queue, {
            durable: false
        });
     });
});

Queremos garantir a existência de uma fila para que o consumidor não fique iniciando antes do emissor. É exatamente isso que o channel.consume faz:

console.log(' [*] Esperando por mensagens em %s. Para sair pressione CTRL+C', queue);
channel.consume(queue, function (msg) {
    console.log(' [x] Recebida: %s', msg.content.toString());
    }, {
        noAck: true
    });

E o nosso código do consumidor ficaria assim:

let amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function (error0, connection) {
    if (error0) throw error0;
    connection.createChannel(function (error1, channel) {
        if (error1) throw error1;
        let queue = 'hello';
        channel.assertQueue(queue, {
            durable: false
        });
        console.log(' [*] Esperando por mensagens em %s. Para sair pressione CTRL+C', queue);
        channel.consume(queue, function (msg) {
            console.log(' [x] Recebida: %s', msg.content.toString());
        }, {
            noAck: true
        });
    });
});

Executando:

Agora, executando no terminal cada um dos arquivos, temos as seguintes saídas:

Produtor de mensagens:

Consumidor:

E assim está feito o nosso canal de emissão de mensagens!

Gostou deste artigo? Comente abaixo!

Exercício fácil: async e await

Async e await são extensões das promises. Quando uma função assíncrona é chamada, ela retorna uma promise. Uma função assíncrona pode conter uma palavra-chave await, que pausa a execução da função e espera pela resolução da promise passada, retomando a execução após a resolução da promise e retornando o valor resolvido. A proposta das funções async/await é simplificar o uso de promises. Assim como promises são similares a callbacks estruturados, as funções async/await são similares à junção de generators com promises. Caso você não conheça o async/await, recomendo que antes de prosseguir, leia este artigo: Entenda o async e await.

Dito isto, vamos ao exercício:

Passo 1:

Crie uma função que após 5 segundos, dobre o resultado do número passado como parâmetro. Essa função deverá retornar uma promise. Use o setTimeOut como temporizador.

O corpo da função abaixo servirá como base para este exercício:

function dobrarEm5Segundos(x) {
  return new Promise( => {
    setTimeout(() => {      
    });
  });
}

Realizado este passo, podemos seguir adiante:

Passo 2:

Crie uma função assíncrona que irá receber um parâmetro x. Crie três variáveis: a, b e c dentro do corpo da função. Cada uma dessas variáveis receberá um await da função do passo anterior. Você deve passar como parâmetros dessa função os número 10, 20 e 30, respectivamente.

Retorne a soma de todos esses elementos, inclusive o parâmetro da função assíncrona.

async function addAsync(x) {
  let a;
  let b;
  let c;
  return;
}

Para mostrar o resultado no console, utilize o .then():

addAsync().then(() => {
  console.log();
});

Você pode realizar o download do exercício resolvido aqui:

[download id=”3580″]

Gostou deste exercício? Comente abaixo:

Entenda o async e await

Async e await são extensões das promises. Caso você não conheça promises, acesse o artigo seguinte e realize a leitura antes de prosseguir com async e await: https://www.mundojs.com.br/2020/03/02/o-que-sao-e-como-criar-promises/.

Quando uma função assíncrona é chamada, ela retorna uma promise. Uma função assíncrona pode conter uma palavra-chave await, que pausa a execução da função e espera pela resolução da promise passada, retomando a execução após a resolução da promise e retornando o valor resolvido.

A proposta das funções async/await é simplificar o uso de promises. Assim como promises são similares a callbacks estruturados, as funções async/await são similares à junção de generators com promises.

async:

A palavra async antes de uma função significa que a função sempre retorna uma promise. Outros valores serão agrupados em uma promise resolvida automaticamente. Por exemplo, esta função async retorna uma promise resolved com a string “Ola Mundo”:

async function funcaoAsync() {
    return 'Ola Mundo';
}

funcaoAsync().then(console.log);

E nós podemos retornar uma promise explicitamente, que retornaria a mesma coisa:

async function funcaoAsync(){
    return Promise.resolve('Ola Mundo')
}

funcaoAsync().then(console.log);

Então, o async garante que a função retorne uma promise.

Mas há outra palavra-chave, chamada await, que funciona apenas dentro de funções assíncronas.

await:

A palavra-chave await faz com que a função espere até que a promise seja estabelecida e retorne o seu resultado. Confira neste exemplo o seu funcionamento:

async function funcaoAsyncAwait() {
    // Criaremos uma promise que retornará a string 
    // "Promise concluída", após o tempo de 5 segundos.
    let promise = new Promise((resolve, reject) => {
        setTimeout(() => resolve("Promise concluída!"), 5000)
    });

    // Esta variavel resultado recebe o resultado da promise, após sua conclusão.
    let resultado = await promise;
    console.log(resultado);
}

funcaoAsyncAwait();

Nesta função, a execução “pausa” e somente retorna quando passa o tempo de execução da promise. Literalmente falando, o await aguarda a conclusão da promise para retornar o resultado! Então, você verá a string “Promise concluída” após 5 segundos de execução, que é o tempo que estabelecemos para a conclusão da promise.

Observação: Não podemos utilizar o await em função síncronas. Ele funciona somente em funções assíncronas, então, precisamos ter o async em nossa function.

Continue lendo “Entenda o async e await”

Utilizando o File System para atualizar, deletar e renomear arquivos

Dando sequência ao nosso artigo de File System do Node.js, já temos os métodos de ler e criar arquivos. Você pode conferir o artigo anterior no link:

Neste artigo, iremos mostrar os métodos:

  • Update
  • Delete
  • Rename

Vamos lá?

Update:

O file system possui os seguintes métodos:

  • fs.appendFile()
  • fs.writeFile()

O método fs.appendFile() anexa conteúdo no final do arquivo especificado:

// Primeiro criaremos uma requisição para o módulo fs
let fs = require('fs');
// Depois, especificamos o arquivo a ser atualizado
// e colocamos o conteúdo a ser anexado.
fs.appendFile('meuNovoArquivo2.txt', 'Esse é o conteúdo anexado.', function(err){
    if (err) throw err;
    console.log('Atualizado!')
})

E teremos nossa saída:

O método fs.writeFile() substitui o arquivo e conteúdo especificados:

let fs = require('fs');

// Especificamos o arquivo a ser atualizado
// e o novo conteúdo
fs.writeFile('meuNovoArquivo2.txt', 'Conteúdo atualizado', function (err){
    if (err) throw err;
    console.log('Salvo!')
})

E a nossa saída será:

Delete:

Para excluirmos um arquivo com o file system, podemos utilizar o método fs.unlink(). Veja na prática:

let fs = require('fs');

// Especificamos o nome e extensão do arquivo a ser deletado
fs.unlink('meuNovoArquivo.txt', function (err){
    if (err) throw err;
    console.log('Arquivo deletado!');
})

E pronto! O arquivo “meuNovoArquivo.txt” foi deletado!

Renomear:

Podemos utilizar o método fs.rename() para renomear um arquivo:

let fs = require('fs');

// Especificaremos o arquivo a ser renomeado
// e o novo nome
fs.rename('meuNovoArquivo2.txt', 'arquivoRenomeado.txt', function(err){
    if (err) throw err;
    console.log('Arquivo renomeado!');
})

E teremos:

E assim, terminamos os artigos obre o File System do Node.js!

Gostou? Comente abaixo!

 

Exercício fácil: Usando Promises

Olá pessoal! Neste exercício iremos praticar um pouco o uso de promises. As promises vem do termo “promessa”, que representa um valor que pode estar disponível em algum momento no futuro, no presente, ou nunca estar disponível. Ele é um objeto utilizado em processamento assíncrono.

Caso você ainda não saiba o que são promises, verifique o link abaixo antes de prosseguir com o seu exercício:

Vamos ao exercício:

Primeiramente, crie um arquivo chamado scripts.js. Neste arquivo, iremos criar uma função de soma que precisará ter como resultado um número par. Para isso, você precisará criar os seguintes callbacks:

  • callbackSucesso() = trará uma mensagem de sucesso, dizendo que a operação foi concluída com sucesso e que o número somado é par.
  • callBackError() = trará uma mensagem de erro, dizendo que a operação falhou, trazendo um número impar (resultado da soma).

Tome como base o seguinte código:

function somar(){
    if (){
        callbackSucesso();
    } else {
        callbackError();
    }
}

function callbackSucesso(){

}

function callbackError(){
 
}

É muito importante a realização deste exercício para darmos continuidade!

Transformando o exercício em uma promise:

Utilize a função anterior para criar uma promise que tenha:

  • then: uma mensagem de sucesso, informando que a operação foi concluída com sucesso, dizendo se é par ou ímpar e trazendo o número;
  • catch: uma mensagem de erro, informando que a operação possui erro, dizendo se é par ou ímpar e trazendo o número.

Dica: Utilize uma promise com função (resolve, reject).

Observação: Conhecimentos aritméticos serão necessários para a realização deste exercício.

Use este arquivo js como referência:

function soma (){
    let p = new Promise() {        
        if (){
            resolve();
        } else {
            reject();
        }
    })
    
    p.then(() {
    }).catch(() {
    })
}

 

Resolução:

Segue abaixo o arquivo de solução para as respectivas fases do exercício

[download id=”3554″]

 

 

Utilizando o File System para leitura e criação de arquivos

O módulo File System do Node.js permite o trabalho com o sistema de arquivos do computador. Podemos incluir o File System no nosso arquivo js utilizando o método require:

let fs = require('fs');

Para que serve o módulo File System? O que podemos fazer com ele?

  • Ler arquivos
  • Criar arquivos
  • Atualizar arquivos
  • Deletar arquivos
  • Renomear arquivos

Para ler arquivos usando o file system:

Podemos utilizar o método fs.readFile() para ler arquivos salvos no computador. Por Exemplo, podemos ter o seguinte arquivo html (que esteja localizado na mesma pasta de execução do node):

<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>readFile()</title>
</head>
<body>
    <h1>Cabeçalho da página</h1>
    <p>Parágrafo da página</p>
</body>
</html>

Após isso, criemos um arquivo javascript (node) para ler o nosso arquivo html e retornar o conteúdo:

// criando uma requisição http para criação do servidor
let http = require('http');

// criando uma requisição para o módulo filesystem
let fs = require('fs');

// criando um servidor funcionando na porta 8080
// e utilizando o fs.readFile para ler o arquivo html
http.createServer(function (req,res){
    fs.readFile('index.html', function(err, data){
        res.writeHead(200, {'Content-Type': 'text/html'});
        res.write(data);
        res.end();
    });
}).listen(8080);

A nossa saída deve ser assim:

E como podemos criar arquivos com o File System?

Podemos utilizar o módulo para criar novos arquivos utilizando os métodos:

  • fs.appendFile()
  • fs.open()
  • fs.writeFile()

O método fs.appendFile() anexa o conteúdo especificado a um arquivo. Se o arquivo não existir, ele será criado. Crie um novo arquivo chamado appendFile.js e faça o seguinte:

// Crie uma requisição para o módulo file system
let fs = require('fs');

// Utilize o appendFile() para criar um novo arquivo que será anexado à sua pasta node
// onde 'meuNovoArquivo.txt será o arquivo a ser criado
// 'Eu sou o novo conteudo é o conteúdo do arquivo txt

fs.appendFile('meuNovoArquivo.txt', 'Eu sou o novo conteudo', function (err){
    // Se ocorrer um erro, especifique
    if (err) throw err;
    // Senão, logue no console = salvo!
    console.log("salvo!")
})

Perceba que o arquivo foi criado na mesma pasta de origem:

Podemos também utilizar o método fs.open() para criar um arquivo vazio:

let fs = require('fs');

// 'w' significa 'write' = escrever
fs.open('meuNovoArquivo2.txt', 'w', function (err, file){
    if (err) throw err;
    console.log("Aberto!");
});

E teremos nosso arquivo criado:

E, finalmente, o método writeFile():

O método writeFile() substitui o arquivo e conteúdo especificados, se existirem. Se o arquivo não existir, ele será criado:

let fs = require('fs');

// Substituiremos o conteúdo em meuNovoArquivo2.txt pela frase
// 'Novo conteúdo!'
fs.writeFile('meuNovoArquivo2.txt', 'Novo conteúdo!', function (err){
    if (err) throw err;
    console.log('Salvo!')
})

E esse será o resultado:

Esses são os métodos de leitura e criação de arquivos utilizando o File System do NodeJS!

Gostou deste artigo? Acompanhe os próximos artigos e comente abaixo!

Novas funcionalidades DevTools

O Google Chrome fornece um conjunto robusto de ferramentas integradas diretamente no navegador, chamado afetivamente de DevTools do Chrome. Essas ferramentas são atualizadas o tempo todo pela equipe de desenvolvedores do Google Chrome; portanto, às vezes vale a pena procurar no site do DevTools para ver as novidades.

Alguns dos recursos menos conhecidos se enquadram na API do utilitário do console. Isso significa que eles estão disponíveis apenas para uso no console do Chrome. Você não pode usar esses recursos dos seus scripts.

  1. $ e $$

Esses dois caras são pseudônimos. Um único $ é um atalho para document.querySelector () e um duplo $$ é um atalho para document.querySelectorAll ().

Digamos que tenhamos um documento HTML simples parecido com este:

<div class="container">
    <div id="app">
        <div :class="{ ugly: isUgly }">{{ message }}</div>
        <h2 id="theCount">The count is: {{ count }}</h2>
        <button class="btn btn-primary" @click="increment">+</button>
        <button class="btn btn-primary" @click="decrement">-</button>
        <button class="btn btn-primary" @click="applyUgly">make ugly</button>
    </div>
</div>

E vamos especular ainda mais que queremos selecionar o primeiro botão deste documento, bem como uma coleção de todos os botões. Dê uma olhada neste:

O único $ retorna um elemento enquanto o $$ retorna a coleção.

Observe que, se você estiver usando uma biblioteca como o jQuery, que atribuirá outro valor a $, substituirá a funcionalidade interna.

  1. A função inspect()

Este pode ser usado em conjunto com o código anterior. Digamos que nosso documento HTML seja enorme e gostaríamos de selecionar um botão no HTML no console. Se inserirmos o seguinte:

Assim que pressionar enter, o DevTools passará da guia Console para a guia Elementos e destacará o elemento HTML que foi selecionado.

Também podemos usar o atalho $$ com inspect(). Ele exibirá uma lista de correspondências no console e podemos clicar em uma para ir para ela no DOM.

 

  1. key / values

Este é útil para ver rapidamente do que um objeto é composto. Digamos que temos o seguinte objeto simples.

let myObj = {
    first: 'alex',
    last: 'ritzcovan',
    state: 'ny'
}

 

Usando keys() e values(), podemos extrair exatamente isso ou podemos exibir as chaves e os valores chamando o objeto diretamente, como mostrado abaixo:

 

  1. Table

Por último, temos a função table(). Usando o mesmo objeto que usamos anteriormente, se o passarmos para table(), obtemos o seguinte no console.

Referência: https://medium.com/dailyjs/5-chrome-devtools-utilities-you-need-to-know-5bfe58c75773

Gostou? Comente abaixo!

Conheça o Callback Hell e como evitá-lo

Conheça o Callback Hell

JavaScript assíncrono, ou JavaScript que usa callbacks, é complicado de acertar intuitivamente. A causa do call-back hell é quando as pessoas escrevem JavaScript de uma maneira que ocorre a execução de cima para baixo, visualmente falando. Em algumas outras linguagens, como C, Python, existe a expectativa de que o que quer que aconteça na linha 1, tenha que terminar antes que o código da linha 2 execute, e assim por diante. Mas no JavaScript, isso funciona de uma maneira diferente.

O que são Callbacks?

São apenas o nome de uma convenção para o uso de funções JavaScript. Não existe nada chamado “callback” no JavaScript, sendo apenas uma convenção. Em vez de utilizar retorno imediato como a maioria das funções. As funções call-back demoram algum tempo para produzir resultados. A palavra “assincronismo”, conhecida no JavaScript como “async”, significa “demora algum tempo / acontece no futuro”.

Quando você chama uma função normal, pode usar seu valor de retorno:

let resultado = multiplicarDoisNumeros(5, 10);

console.log(resultado);

No entanto, funções assíncronas e que usam call-backs não retornam nada imediatamente:

let foto = downloadFoto('http://coolcats.com/cat.gif')

// foto é 'undefined'!

Nesse caso, o gif pode demorar muito para ser baixado e você não deseja que seu programa seja pausado enquanto aguarda a conclusão do download. Em vez disso, você armazena o código que deve ser executado logo após a conclusão do download em uma função. Este é o callback. Você executa a função downloadFoto e ele executará seu callback, quando o download estiver concluído e passará a foto (ou um erro se algo der errado).

downloadFoto('http://coolcats.com/cat.gif', lidarComFoto)

function lidarComFoto (error, foto) {
  if (error) console.error('Erro de Download!', error)
  else console.log('Download finished', foto)
}

console.log('Download iniciado')

Como podemos evitar o callback hell?

  1. Mantenha seu código limpo:

Callback hell geralmente é causado por más práticas de codificação. Escreva melhor o seu código e não passará trabalho.

let form = document.querySelector('form');
form.onsubmit = function formSubmit (submitEvent) {
    let name = document.querySelector('input').value;
    request({
        uri: "http://example.com/upload",
  body: name,
        method: "POST"
        }, function postResponse (err, response, body) {
        let statusMessage = document.querySelector('.status');
  if (err) return statusMessage.value = err;
  statusMessage.value = body;
    })
}

Nomear funções é uma prática benéfica e facilita a leitura do código.

Agora podemos mover as funções para o nível superior do nosso programa:

document.querySelector('form').onsubmit = formSubmit;

function formSubmit (submitEvent) {
  let name = document.querySelector('input').value;
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, postResponse)
}

function postResponse (err, response, body) {
  let statusMessage = document.querySelector('.status');
  if (err) return statusMessage.value = err;
  statusMessage.value = body
}
  1. Modularize:

Segundo Isaac Schlueter (do projeto Node.js): “Escreva pequenos módulos que cada um faz uma coisa e monte-os em outros módulos que fazem uma coisa maior. Você não terá um callback hell se não for lá”.

Criando um módulo, ficaria assim:

module.exports.submit = formSubmit

function formSubmit (submitEvent) {
  let name = document.querySelector('input').value;
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, postResponse)
}

function postResponse (err, response, body) {
  let statusMessage = document.querySelector('.status');
  if (err) return statusMessage.value = err;
  statusMessage.value = body;
}
  1. Lide com todos os erros:

Existem diferentes tipos de erros: de sintaxe, de runtime, erro de permissões, falha no disco rígido, entre outros. Ao lidar com callbacks, você, por definição, está lidando com tarefas despachadas, executa algo em segundo plano e, em seguida, conclui com êxito ou aborta devido a alguma falha. Podemos manipular isso deixando o primeiro argumento da função callback reservada ao erro.

function lidarComFoto (error, foto) {
  if (error) console.error('Erro de Download!', error)
  else console.log('Download finished', foto)
}

Dito isto, sempre lide com todos os erros e mantenha seu código simples e limpo.

Para maiores informações, acesse o site: http://callbackhell.com/

Referências: http://callbackhell.com/